python實戰:破解大衆點評用CSS加密數字的反爬機制

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

程序運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章