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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44530979/article/details/88084581

**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()

程序运行结果:
在这里插入图片描述

展开阅读全文

没有更多推荐了,返回首页