1. 程式人生 > 實用技巧 >python爬蟲實戰---爬取大眾點評評論

python爬蟲實戰---爬取大眾點評評論

python爬蟲實戰—爬取大眾點評評論(加密字型)

1.首先開啟一個店鋪找到評論

很多人學習python,不知道從何學起。
很多人學習python,掌握了基本語法過後,不知道在哪裡尋找案例上手。
很多已經做案例的人,卻不知道如何去學習更加高深的知識。
那麼針對這三類人,我給大家提供一個好的學習平臺,免費領取視訊教程,電子書籍,以及課程的原始碼!
QQ群:101677771

2.分析網頁

檢視到下面有些字型經過加密處理 重新整理頁面會發現 每一次加密的字型是不一樣的

3.傳送請求獲取資料

檢視網頁原始碼 檢視所有css 發現這個css就是我們想要用的檔案 那麼現在我們就要用程式碼來獲取到這個css檔案的urlCookie自行更換


程式碼實現:

class DownComment:

    def __init__(self):
        # 爬取資料cookie user—agent
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6"
                          ") AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36",
            "Cookie": 'fspop=test; _lxsdk_cuid=1741e6d406ec8-07a55a88376aea-31657305-13c680-1741e6d406ec8; _lxsdk=1741e6d406ec8-07a55a88376aea-31657305-13c680-1741e6d406ec8; _hc.v=686b52bb-73c6-234a-0599-c881b393882d.1598238311; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1598238354; cityid=838; default_ab=index%3AA%3A3; switchcityflashtoast=1; s_ViewType=10; ll=7fd06e815b796be3df069dec7836c3df; ua=dpuser_7474971098; ctu=4cc4b902d60a40f51447c2d6d386233260a8f2e43bf520fb73056aa472dfbb35; aburl=1; Hm_lvt_dbeeb675516927da776beeb1d9802bd4=1598270129; Hm_lpvt_dbeeb675516927da776beeb1d9802bd4=1598270129; cy=1; cye=shanghai; dper=627d6236bc87ce08b3d5c48661e5572f504bcf9938fee451ebd4566d8234bc5b1ad10791c702986d1398b6a838a4e550619d42c3d68d02b0f53cf4ed5c38702b47d41ef5f7e7d368892b8be8a46b2eb844582afbcc419e5e28df0a92c1df589e; uamo=17643530928; dplet=7731f44d071e7840935794d1a9ae35d4; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1598342331; _lxsdk_s=1742497507a-072-c5-68e%7C%7C766'
        }
        # 爬取大眾點評的url
        self.url = None
        # 頁面返回的text
        self.text = None
        # css檔案的內容
        self.css_content = None
        # css檔案的url
        self.css_url = None
        # 取出的字型檔案的內容
        self.svg_content = None
        # 用來儲存每一個字的對映關係的列表
        self.font_d_l = list()
        # 用來儲存座標對映
        self.position_l = list()
        # 字型位置
        self.position_list = list()
        # 資料
        self.data = list()


    def down_css(self):
        """
        獲取css檔案
        :return:
        """
        # 請求返回的text
        self.text = requests.get(self.url, headers=self.headers).text
        # 使用xpath取出所有link中的連結
        x = etree.HTML(self.text)
        css_list = x.xpath('//link/@href')
        self.css_url = 'https:' + str(re.findall('//s3plus\.meituan\.net.+?\.css', ' '.join(css_list))[0])

4.繼續分析我們需要的東西

開啟這個css檔案 發現上一個頁面加密的字型的類 在這個css檔案中可以用查詢到 後面有對應的座標

5.嘗試在css中找尋字型檔案

查詢css檔案中 有沒有我們想要的字型檔案 command + f 或者 ctrl
+f 查詢 發現檔案中有三個字型檔案 分別開啟三個檔案的url 發現只有一個字型對映檔案是正確的

6.找出正確的字型檔案

開啟三個字型檔案的url 發現正確的就是最多的 也就是最大的一個檔案 我們不能憑著url來判斷哪個字型檔案更大 所以要訪問 根據返回的資料 來判斷正確的檔案是哪個 接下來就要訪問url來獲取字型檔案的內容 然後將最大的字型檔案內容儲存起來 方便替換

    def down_svg(self):
        """
        下載字型檔案
        :return:
        """
        # css請求返回的text
        self.css_content = requests.get(self.css_url, headers=self.headers).text
        # 使用正則取出
        svg_list = re.findall(r"background-image: url\((.+?)\);", self.css_content)
        svg_url = ["https:{}".format(svg) for svg in svg_list]

        # 下載最大的svg檔案
        length_d_l = list()
        [length_d_l.append({"len": len(requests.get(svg).text), "content": requests.get(svg).text}) for svg in svg_url]
        self.svg_content = str([x["content"] for x in length_d_l if x["len"] == max([i["len"] for i in length_d_l])][0])

7.由於是動態重新整理 將網頁資料儲存到本地並分析 找到(x,y)和字型的對映關係

取出字型檔案後可以在本地進行手動的查詢 摸索文字對應關係 因為大眾點評每一次重新整理都是動態更改css或者svg檔案內容 包括每次重新整理加密的字都不同 所以將一個儲存到本地 根據本地這一個固定的來嘗試 嘗試成功後動態獲取 經過多次嘗試 發現規律 兩個數字第一個數字除以14
就是文字的下標
此圖數字為-406 除以 14 下標 就是29 第二個數字就在兩個y值中間 根據規律 匹配出所有文字的對映關係

!!!此段程式碼只是為了儲存資料方便分析和爬取資料的程式碼無關 !!!

        with open("xx.html", "w")as f:
            f.write(self.text)
        with open('xx.css', "w") as f:
            f.write(self.css_content)
        with open("font.svg", "w") as f:
            f.write(self.svg_content)

8.根據找到的規律 取出字型檔案中所有字型 還有位置 儲存到字典中

用正則在字型檔案中取出數字的x值(即在本行的下標)y值用一個元組來儲存 判斷時 獲取加密文字的座標y值是否在元組兩個值中間即可
取出所有的資料 儲存到類中的字典
程式碼實現:

    def font_mapping(self):
        """
        字型對映
        :return:
        """
        # 使用正則取出字
        font_list = re.findall(r'<text x=".*" y="(.*)">(.+?)</text>', self.svg_content)
        # 迴圈並將對映新增到列表中
        for num, i in enumerate(font_list):
            for x, v in enumerate(i[1]):
                self.font_d_l.append({
                    "value": v,
                    "x": x,
                    "y": (int(font_list[num - 1][0]) if font_list[num - 1][0] != '2495' else 0, int(i[0]))
                })

儲存後的字典格式為 value值為字型內容 x為下標 y值為一個元組 用來儲存在哪兩個數字之間

9.現在開始獲取所有css檔案中的類對應的座標

依舊使用正則來取出所有的資料 查詢到的資料 再儲存到類中的一個字典
程式碼實現:

    def position_mapping(self):
        '''
        位置對映
        :return:
        '''
        all_ = re.findall("\.(.+?){background:-(.+?)\.0px -(.+?)\.0px;}", self.css_content)
        [self.position_l.append({
            "class": i[0],
            "x": i[1],
            "y": i[2],
        }) for i in all_]

字典的格式為:

10.取出所有加密的字

現在取出網頁中被加密的字型的class屬性 使用xpath取出就可以
程式碼實現:

    def all_font_position(self):
        """
        所有字型位置
        :return:
        """
        x = etree.HTML(self.text)
        self.position_list = x.xpath('//svgmtsi/@class')

11.解密文字

因為現在已經取出了兩個字典 一個有加密字型的class屬性還有字型的x,y的值 另一個字典中有這個加密字型對應的文字 剛剛我們也取出來所有被加密文字的class屬性 只需要迴圈判斷 取出對應的字就可以來
程式碼實現:

    def find_font(self, x, y):
        '''
        查詢具體字型
        :param x:
        :param y:
        :return:
        '''
        # 根據座標返回對應的字型
        new_x = int(x) / 14
        for i in self.font_d_l:
            if int(i.get("x")) == int(new_x) and i.get('y')[0] < int(y) < i.get('y')[1]:
                return i.get('value')

12.替換所有加密文字為解密後文字

將原來儲存的這個網頁的text內容中加密的文字 替換為正常的文字
程式碼實現:
這段程式碼在最後的執行方法中 就是主方法中

        str_text = str(self.text)
        for position in self.position_list:
            for x in self.position_l:
                if x.get("class") == position:
                    str_text = str_text.replace('<svgmtsi class="{}"></svgmtsi>'.format(position),
                                                str(self.find_font(x.get('x'), x.get('y')))).replace("&#x0A;",
                                                                                                     '').replace(
                        "&#x20;", '')

13.獲取資料

最難的加密已經弄出來了 現在就是一個簡單的取資料就可以了 因為大眾點評的長評論和短評論儲存的xpath不同 所以需要一個小判斷 直接看程式碼吧
程式碼實現:

  def get_data(self, str_text):
        x = etree.HTML(str_text)
        # 取出所以li標籤
        li = x.xpath('//div[@class="reviews-items"]/ul/li')
        print(len(li))
        for l in li:
        	# 定義一個字典用來儲存資料
            item = dict()
            # 口味評分
            flavor = l.xpath("./div/div/span/span[1]/text()")
            # 環境評分
            ambient = l.xpath("./div/div/span/span[2]/text()")
            # 服務評分
            service = l.xpath("./div/div/span/span[3]/text()")
            # 人均價格
            price = l.xpath("./div/div/span/span[4]/text()")
            # 釋出時間
            times = l.xpath("./div[@class='main-review']/div/span[@class='time']/text()")
            # 短評論
            s_comment = l.xpath("div[@class='main-review']/div[@class='review-words']")
            # 長評論
            l_comment = l.xpath("div[@class='main-review']//div[@class='review-words Hide']")
            # 儲存到字典中
            item["flavor"] = str(flavor[0]).replace('\n', '').replace(' ', '') if flavor else "暫無"
            item["ambient"] = str(ambient[0]).replace('\n', '').replace(' ', '') if ambient else "暫無"
            item["service"] = str(service[0]).replace('\n', '').replace(' ', '') if service else "暫無"
            item["price"] = str(price[0]).replace('\n', '').replace(' ', '') if price else "暫無"
            item["time"] = str(times[0]).replace('\n', '').replace(' ', '') if times else "暫無"
			# 判斷此條評論為長評還是短評 然後儲存到字典
            if l_comment:
                l_str = html.unescape((etree.tostring(l_comment[0]).decode()))
                l_com = re.findall('<div class="review-words Hide">(.+?)<div class="less-words">', l_str,
                                   re.DOTALL)[0]
                item["comment"] = l_com.replace('\n', '').replace(' ', '').replace('\t', '')
            elif s_comment:
                s_str = html.unescape((etree.tostring(s_comment[0]).decode()))
                s_com = re.findall('<div class="review-words">(.+?)</div>', s_str, re.DOTALL)[0]
                item["comment"] = s_com.replace('\n', '').replace(' ', '').replace('\t', '')
            else:
                item["comment"] = "該使用者沒有填寫評論..."
            # 類中的列表 來儲存儲存後的字典
            self.data.append(item)

14.儲存資料到csv檔案中

程式碼實現:

    def save(self):
        """
        儲存資料為csv檔案
        :return:
        """
        pandas.DataFrame(self.data,
                         columns=["flavor", "ambient", "service", "price", "time", "comment"]).to_csv(
            'data.csv')

15.寫一個呼叫所有方法的主方法

程式碼實現:

    def run(self, url):
        self.url = url
        # 獲取css
        self.down_css()
        # 獲取字型檔案
        self.down_svg()
        # 新增字型對映
        self.font_mapping()
        # 新增位置對映
        self.position_mapping()
        # 獲取所有加密字型位置
        self.all_font_position()
        # 查詢對應字型 並替換
        str_text = str(self.text)
        for position in self.position_list:
            for x in self.position_l:
                if x.get("class") == position:
                    str_text = str_text.replace('<svgmtsi class="{}"></svgmtsi>'.format(position),str(self.find_font(x.get('x'),x.get('y')))).replace("&#x0A;",'').replace("&#x20;", '')
        # 獲取資料
        self.get_data(str_text)
        # 儲存檔案
        self.save()
        # 控制檯列印資料
        pprint(self.data)

16.執行程式碼

程式碼如下:

def main():
    # 建立爬蟲物件
    down_spider = DownComment()
    # 爬取5頁資料
    for i in range(1, 2):
        print('-----------當前為{}頁---------------'.format(i))
        url = "http://www.dianping.com/shop/k9oYRvTyiMk4HEdQ/review_all/p{}".format(i)
        down_spider.run(url=url)

17.檢視資料 儲存資料

執行程式碼 檢視資料即可 控制檯列印以及儲存到本地的csv檔案 感謝你的觀看 希望對你有所幫助
注:網站的反扒策略一直在變 有可能一小時前可以 有可能一小時後就不能使用了