1. 程式人生 > >爬取大眾點評之初步試探

爬取大眾點評之初步試探

常規的反爬機制有訪問頻率限制、cookie限制、驗證碼、js加密引數等。目前解決不了的js加密是今日頭條的_signature引數、京東的s引數(在搜尋結果的ajax中,返回的結果根據s引數的不同而不同,目前沒有發現規律)、新版12306登陸時的callback引數等

而今天的網站的反爬機制是目前我見過的最有水平的,網址:http://www.dianping.com/, 以上的反爬機制它都有,而且一些關鍵資訊全部通過css來控制圖片的偏移量顯示出來。

隨便選擇一個商家,比如 http://www.dianping.com/shop/92465668, 其中商家名稱下的一些資訊中的數字全部是以圖片的形式顯示的,每個數字都是,就連地址中的文字大部分也是圖片,還有評論中的文字等。
比如1706條評論的HTML程式碼如下:

<span id="reviewCount" class="item">
  1
  <span class="kj-ouAp"></span>
  <span class="kj-YuRe"></span>
  <span class="kj-QXcp"></span>條評論
</span>

首先找到對應的css檔案
//s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/499267ad9083423317049ce463a855fe.css

搜尋一下上面的class,發現一下程式碼:

span[class^="kj-"] {
	width: 14px;
	height: 30px;
	margin-top: -9px;
	background-image: url(//s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/f6cfde1f84946b2e60fe15c9d0470ae3.svg);
	background-repeat: no-repeat;
	display: inline-block;
	vertical-align: middle;
	margin-left: -6px;
}
.kj-ouAp {
	background: -120.0px -7.0px;
}

背景圖片中的svg檔案顯示的是10個數字,而.kj-ouAp的css樣式則控制背景圖片的位置,因為span標籤限定了大小,所以正好顯示svg圖片中的某一個數字。

爬蟲思路:要想得到相應的資料,需要先請求初始網頁得到css檔案和相應資料的span標籤的css屬性(其中1是直接顯示的,也要拿到),再從css檔案中提取除svg檔案和一些css屬性的偏移量,這裡只需要第一個偏移量如 -120px,因為圖片只有一行,第二個偏移量都是一樣的。

svg檔案是xml定義的,可以文字格式開啟直接得到10個數字的文字和對應的位置。再通過上面拿到的span標籤的css屬性和位置比較一下,轉化為相應的數字(這裡有一個簡單思路,不需要位置,直接去掉負號排序,那麼css屬性就和拿到的10個數字相對應了)

程式碼如下:

# -*- coding: utf-8 -*-
"""
date: Mon Nov 26 14:55:02 2018
python: Anaconda 3.6.5
author: kanade
email: [email protected]
"""
import requests
import re
import pyquery


headers = {
        'Host':'www.dianping.com',
        'Referer':'http://www.dianping.com/beijing/ch10/g110',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'
   }

url = 'http://www.dianping.com/shop/92465668'
resp = requests.get(url, headers=headers)

html = resp.text
css_url_regex = re.compile(r'href="//(s3plus.meituan.net.*?)\"')
css_url = re.search(css_url_regex, html).group(1)
css_url = 'http://' + css_url

css_resp = requests.get(css_url)
regex_kj_svg = re.compile(r'span\[class\^="kj-"\][\s\S]*?url\((.*?)\)')
css_html = css_resp.content.decode('utf-8')
svg_kj_url = re.search(regex_kj_svg, css_html).group(1)
svg_kj_url = 'http:' + svg_kj_url

svg_kj_resp = requests.get(svg_kj_url)
svg_kj_html = svg_kj_resp.text
number = re.search(r'\d{10}', svg_kj_html).group()


regex_kjs_css = re.compile(r'\.(kj-\w{4})[\s\S]*?-(\d+)')
kjs = re.findall(regex_kjs_css, css_html)
kjs = {i[0]:int(i[1]) for i in kjs}

temp = sorted(kjs.items(), key=lambda x:x[1])

kjs = {temp[i][0]:number[i] for i in range(10)}


#x = svg_kj_doc('text').attr.x
#x = [int(i) for i in x.split(' ')]

doc = pyquery.PyQuery(html)

regex = r'\d|kj-\w{4}'
review_html = doc('#reviewCount').html()
temp = re.findall(regex, review_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
review = int(''.join(temp))
print('評論數:', review)

avgprice_html = doc('#avgPriceTitle').html()

temp = re.findall(regex, avgprice_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
avgprice = int(''.join(temp))
print('平均價格:', avgprice)

kouwei_html = doc('#comment_score .item:first-child').html()
temp = re.findall(regex, kouwei_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
kouwei = '.'.join(temp)
print('口味評分:', kouwei)
    
huanjing_html = doc('#comment_score .item:nth-child(2)').html()
temp = re.findall(regex, huanjing_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
huanjing = '.'.join(temp)
print('環境評分', huanjing) 

fuwu_html = doc('#comment_score .item:nth-child(2)').html()
temp = re.findall(regex, fuwu_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
fuwu = '.'.join(temp)
print('服務評分', fuwu) 

tel_html = doc('.expand-info.tel').html()
temp = re.findall(regex, tel_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
temp.insert(3, '-')
tel = ''.join(temp)
print('電話評分', tel) 

因為需要拿到文字1和span標籤的class屬性,用pyquery暫時不知道怎麼拿到資料,主要是還要順序。所以我採用pyquery拿到評論的一段程式碼,再用正則提取出資料。在測試的時候,如果兩次執行間隔小的話,會跳出驗證碼。拿到這些資料並不需要登陸,所以可以直接換代理。