**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()
程序運行結果: