您的位置:首页 > 编程语言 > Python开发

python实战:破解大众点评用CSS加密数字的反爬机制

2019-03-03 10:01 816 查看

**1、**此次我们以该站点:惠州粤菜推荐 为目标站点,来到站点后打开chrome的开发者工具,点击刷新页面,按下图操作你会看到点评数中部分数字被进行了加密,所显示的只有span标签和class 并没有数字信息。

点击其中的某个标签,通过查看css信息就会发现在里面有个网址(即上图中的【4】),点击打开该网址你会看到下图:

其实这些数字是SVG矢量图,SVG矢量图是基于可扩展标记语言,用于描述二维矢量图形的一种图形格式,通过使用不同的偏移量就能显示不同的字符。而在第一张图中我们可以看到rtsj0这个css class里面有一个background属性,其定义了背景图片偏移的位置,所以点评网上显示数字的原理就是通过设置不同的偏移位置,显示背景图片相应位置上的数字。

**2、**知道其原理后,我们需找到定义这些css的文件及相应的svg矢量图,按照第一张图的操作我们点击【3】,会跳转到下图:

从上图中我们就能知道各种css是如何定义偏移量的,且从backgro-image这个属性里能得到svg矢量图的地址,要想知道这个svg矢量图地址我们就得先知道这个css文件是如何请求到的。前期经过多次测试发现这个css文件的网址是会变化的,因此我们需让程序去找到这个css文件的地址而不是每次都手动寻找。我们先按下图进行操作
再切换到Elements中按下图操作进行搜索:

找到这个css文件后,我们就可以通过正则表达式将该地址提取出来(提取规则看最后源码),提取出来后请求这个URL地址会到下图这个页面:

在上图中就有包含svg矢量图的地址,我们再次用正则表达式提取(提取规则看最后源码)就能得到backgro-image这个属性的值后再去请求就得到svg矢量图。接下来就是该如何解密这个偏移量来找到对应的数字,我在这篇博客 https://blog.csdn.net/weixin_43796109/article/details/86506050 中的解密方法受到启发。

**3、**知道解密的方式后编写代码,以下是我的全部源码:

import re
from lxml import etree
from requests_html import HTMLSession
class DaZhong():
def __init__(self):
#此处以爬取第一页中的评论数为例
self.stat_url='http://www.dianping.com/huizhou/ch10/g103'

def parse_url(self,url):
session = HTMLSession()
response = session.get(url)
return response.content.decode()

#在页面找到定义这些css的URL地址
def get_css_url(self):
html = self.parse_url(self.stat_url)
svgtextcss = re.search(r'href="([^"]+svgtextcss[^"]+)"', html, re.M)
css_url = svgtextcss.group(1)
return css_url

#我们从定义偏移量的css文件里找到背景图片的路径,并获取SVG返回的数据
def get_svg(self):
content = self.parse_url('https:'+self.get_css_url())
svg = re.search(r'span\[class\^="rt"\].*?background\-image: url\((.*?)\);', content)
svg_url = svg.group(1)
svg_html = self.parse_url('https:'+ svg_url)
return svg_html

# 获取定义偏移量的css文件后将结果以字典形式存储,以便我们传入页面中任意css名称来获取其对应的偏移量
def get_css_offset(self):
css_html = self.parse_url('https:'+self.get_css_url())
offset_item=re.findall(r'(\.[a-zA-Z0-9-]+)\{background:-(\d+).0px -(\d+).0px',css_html)
result={}
for item in offset_item:
css_class = item[0][1:]
xoffset = item[1]
yoffset = item[2]
result[css_class] = [xoffset,yoffset]
return result

#根据偏移量找到对应的数字
def parse_comment_css(self,xoffset,yoffset):
svg_html = self.get_svg()
pattern = re.compile(r'y=.*?(\d+)">(\d+)</text>', re.S)
items = re.findall(pattern, svg_html)
svg_list=[]
for item in items:
svg={}
svg['y_key']=int(item[0])
svg['text']=item[1]
svg_list.append(svg)
x,y=int(xoffset),int(yoffset)
if y<=svg_list[0]['y_key']:
# print('数字:',svg_list[0]['text'][x//12])
return svg_list[0]['text'][x//12]
elif y<=svg_list[1]['y_key']:
# print('数字:', svg_list[1]['text'][x// 12])
return svg_list[1]['text'][x// 12]
else:
# print('数字:', svg_list[2]['text'][x // 12])
return svg_list[2]['text'][x // 12]

#获取点评数
def get_comment_num(self):
content=self.parse_url(self.stat_url)
html = etree.HTML(content)
shops = html.xpath('.//div[@id="shop-all-list"]/ul/li')#获取到所有店面
css_class_dirt=self.get_css_offset()#获取定义偏移量的css文件后将结果以字典形式存储
for shop in shops:
shop_name=shop.xpath('.//div[@class="tit"]/a/@title')[0]#获取店名
review_num = shop.xpath('.//div[@class="comment"]/a[contains(@class,"review-num")]/b')[0]#获取可见的数字
num = 0
if review_num.text:
num =int(review_num.text)
for review_node in review_num:
css_class = review_node.attrib["class"]#获取css名称
#根据css名称获取其对应的偏移量
xoffset,yoffset=css_class_dirt[css_class][0],css_class_dirt[css_class][1]
# 根据偏移量来找到对应的数字
new_num = self.parse_comment_css(xoffset,yoffset)
num = num * 10 + int(new_num )
print("restaurant: {}, review_num: {}".format(shop_name, num))

if __name__ == '__main__':
dazhong=DaZhong()
dazhong.get_comment_num()

程序运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: