- 前言
之前在做大眾點評網(wǎng)數(shù)據(jù)的時候切心,發(fā)現(xiàn)數(shù)據(jù)在前端顯示是用標簽來替換朽寞。這樣爬蟲采集到的就是一堆標簽加一點內容所混雜的臟數(shù)據(jù)挂洛,同時發(fā)現(xiàn)標簽中的值也是隨時改變的黎比。所以這次也是花了一點時間來整理關于大眾點評JS加密的內容超营,給大家簡單講解一下,以此來學習借鑒如何有效安全的防范爬蟲阅虫。僅供學習參考演闭,切勿用于商業(yè)用途
一、介紹
首先隨便打開大眾點評網(wǎng)一家店颓帝,看到數(shù)據(jù)都是正常狀態(tài)如圖1-1米碰,然后我們用開發(fā)者工具定位到元素上會發(fā)現(xiàn)如圖1-2所示:
我們可以看到數(shù)據(jù)都是用<bb>標簽給替換了,同時我們還發(fā)現(xiàn)如圖1-3 购城、1-4所示:
店面基礎信息用<bb>標簽表示吕座、數(shù)字用<cc>表示、評論用<span>標簽表示瘪板。并且這些被替換的文字也不是固定的吴趴,可能過一會被替換的文字被還原,其余未被替換的文字被替換侮攀。而且標簽值也會隨時間發(fā)生改變锣枝,這樣即使每個標簽都人工標記,過一段時間數(shù)據(jù)還是會亂兰英。
二撇叁、頁面分析
我們隨便查看一個被替換了的標簽元素,發(fā)現(xiàn)它對應了一個文件如圖2-1所示:
可以看到標簽一些基本信息畦贸,長度高度還有和它相關的一個鏈接陨闹,打開這個鏈接,我們可以發(fā)現(xiàn)是一個亂序的中文數(shù)據(jù)表格薄坏。如圖2-2所示:
基本上我們就可以推測出來趋厉,數(shù)據(jù)被隱藏替換的大概原理,通過標簽來對應表格文字颤殴,其中class的值我們可以理解為被替換數(shù)據(jù)內容的key觅廓,標簽類型就好比對應的數(shù)據(jù)表,這里有三種類別標簽就是對應三張不同的數(shù)據(jù)表涵但,這樣我們還需要解決的問題是:
- (1)不同種類的標簽如何對應不同的表杈绸;
-
(2)如何通過標簽的class值去對應被替換的數(shù)據(jù)帖蔓。
所以到這一步,我們還少一些關鍵的線索瞳脓,我們繼續(xù)看到之前頁面塑娇,發(fā)現(xiàn)圖表鏈接包含在一個css表中 如圖2-3所示:
圖2-3 隱藏的css文件
可以看到有一個css文件,我們在元素中搜索這個表 如圖2-4
圖2-4 css文件所在位置
這樣我們便可以從網(wǎng)頁中找到指定的文件鏈接劫侧,關于如何根據(jù)key值找到對應的參照表埋酬。我們打開此鏈接如圖2-5、2-6所示:
圖2-5 key值對照表
我們可以從圖2-5烧栋、圖2-6看到cc標簽写妥、span標簽、bb標簽每個下面都有一個url审姓。我們打開可以發(fā)現(xiàn)就是之前那種格式的對應表珍特,我們稍后再看。我們先看到我們的key值(標簽class屬性對應的值)魔吐,后面都接著一個坐標扎筒,格式統(tǒng)一都是.(標簽class值){background: (橫坐標)px(縱坐標) px;這樣我們就已經獲得key值所對應的坐標,到這里我們基本可以確定酬姆,我們找到key值和所對應的坐標就可以根據(jù)標簽相對應的表嗜桌,利用坐標就可以找到利用標簽所替換的數(shù)據(jù)。
三辞色、JS解密
接下來骨宠,我們就需要知道如何利用獲取到的坐標來獲取正確被替換的數(shù)據(jù)。我們首先根據(jù)那張表格依次打開鏈接淫僻,查看它們元素會發(fā)現(xiàn)有兩種格式诱篷,一種格式有元素defs標簽,另一種沒有雳灵。如圖3-1棕所、圖3-2、圖3-3所示:
總共三張表對應三個不同標簽悯辙,分兩種格式琳省,我分成有defs標簽和無defs標簽兩類。注意躲撰,這里我并沒有根據(jù)標簽種類去劃分表類別针贬,因為每一次獲取到的格式都是隨機的。也就是說今天span標簽是對應有defs標簽數(shù)據(jù)表拢蛋,可能明天就對應不含有defs標簽類的數(shù)據(jù)表桦他。數(shù)據(jù)表格式是隨機變化的。
(1)含有defs標簽類別數(shù)據(jù)表解密
以地址中的bb標簽為例谆棱,看地址所對應的標簽值為pzgoz快压,如圖3-4所示:圆仔,以及bb標簽所對應的svg數(shù)據(jù)表,如圖3-6所示:
根據(jù)css表找到它的對應坐標x=-294,y=-113.0蔫劣,如圖3-5所示:
最后根據(jù)標簽類別找到對應svg數(shù)據(jù)表鏈接坪郭,打開鏈接,如圖3-6所示:
將坐標轉為正脉幢,先從y坐標看到defs標簽d屬性上歪沃,y=113應該排序在d=M0 90 H600與d=M0 128 H600之間,所以id應該為4嫌松,再看textPath標簽xlink:href屬性為#4的就是我們所要找的行沪曙,如果之前有注意看標簽width的話,就知道每一個字長為14px豆瘫,x=294去除以14珊蟀,等于21,這一行做list的話外驱,list[21]應該就是朝字。代碼如下:
hight = html.xpath('//defs//path/@d')
hight = [i.replace('M0','').replace('H600','').strip() for i in hight]
hight.append(y)
hight = sorted(hight, key=lambda i: int(i))
y = hight.index(y)
#若縱坐標與標簽y相等相等時腻窒,取同行的高度
if y != len(hight) - 1 and hight[y] == hight[y + 1]:
y = y + 1
content = html.xpath('//textpath/text()')[y]
content = ''.join(content).strip()
x = int(int(x) / 14)
return content[x]
(2)不含有defs標簽類別數(shù)據(jù)表解密
之前步驟一樣昵宇,直接跳到找到對應的svg數(shù)據(jù)表,如圖3-7所示:
比如數(shù)字3儿子,x=-232.0px y=-140.0px,先全部取正數(shù)瓦哎,y=140 對應在標簽text屬性y=118和y=163之間,同樣排序在第四位柔逼,所以取第四行為list蒋譬,同樣232/14=16,list[16]=3愉适;同時也可以數(shù)標簽text屬性x中232排在224-238之間犯助,也就是16-17位之間,但要取16為下標维咸。代碼如下:
hight = html.xpath('//text/@y')
hight.append(y)
hight = sorted(hight, key=lambda i: int(i))
y = hight.index(y)
# 若縱坐標與標簽y相等相等時剂买,取同行的高度
if y != len(hight) - 1 and hight[y] == hight[y + 1]:
y = y + 1
content = html.xpath('//text/text()')[y]
content = ''.join(content).strip()
x = int(int(x) / 14)
return content[x]
四、代碼實現(xiàn)
這里附上我測試的代碼癌蓖,僅供學習參考瞬哼,切勿用于商業(yè)用途。直接使用前租副,請帶上自己瀏覽器的請求頭參數(shù)坐慰。
#coding=utf-8
import requests
import lxml.html
import re
from decimal import Decimal
#獲取svg表格中被替換的數(shù)據(jù)
def get_hide_char(x,y,type,url):
response = requests_middler(url)
html = lxml.html.fromstring(response.content)
#網(wǎng)頁格式是否有defs元素
if len(html.xpath('//defs//path/@d'))<1:#如果沒有defs標簽
hight = html.xpath('//text/@y')
hight.append(y)
hight = sorted(hight, key=lambda i: int(i))
y = hight.index(y)
# 若縱坐標與標簽y相等相等時,取同行的高度
if y != len(hight) - 1 and hight[y] == hight[y + 1]:
y = y + 1
content = html.xpath('//text/text()')[y]
content = ''.join(content).strip()
x = int(int(x) / 14)
return content[x]
else:#如果含有defs標簽
hight = html.xpath('//defs//path/@d')
hight = [i.replace('M0','').replace('H600','').strip() for i in hight]
hight.append(y)
hight = sorted(hight, key=lambda i: int(i))
y = hight.index(y)
#若hight相等時用僧,取同行的高度
if y != len(hight) - 1 and hight[y] == hight[y + 1]:
y = y + 1
content = html.xpath('//textpath/text()')[y]
content = ''.join(content).strip()
x = int(int(x) / 14)
return content[x]
#請求url統(tǒng)一方式
def requests_middler(url):
headers={'User-Agent':''}#加入自己的User-Agent
html = requests.get(url, headers=headers)
return html
#獲取css表中key值對應的坐標结胀,以及標簽對應svg表鏈接
def get_position_xy(html,password,type,url):
#獲取key值對應的坐標
rule = re.compile(password + '{background:-(.*?).0px -(.*?).0px;')
result = re.findall(rule, html)[0]
x = result[0]
y = result[1]
#獲取標簽所對應的svg表格
rule = re.compile(type + '\[class\^="\w+"\]\{(.*?)}', re.S)
result = re.findall(rule,html)[0]
rule = re.compile('url\((.*?)\)')
href = re.findall(rule, result)
url = 'http:' + href[0]
hide_char=get_hide_char(x,y,type,url)
return hide_char
#解密前數(shù)據(jù)做好清洗两残,清洗成標簽與文字組成的list,保證還原字符串順序
def get_hide_string(s,url):
result=[]
a_list = re.split('</\S+>',s)
# print(a_list)
b=[]
for i in a_list:
try:
dex = i.index('<')
except ValueError:
b.append(i)
continue
if dex!=0:
b.append(i[:dex])
rule = re.compile('<(.*?) class="(.*?)"')
tag = re.findall(rule,i[dex:])[0]
b.append(tag)
try:
b.remove('')
except ValueError:
pass
print(b)#b為已清洗好的list把跨,接下來分別還原標簽替換數(shù)據(jù)
html = requests_middler(url)
html = html.text
for tag in b:
#遇到類別標簽則分類進行篩選
if tag[0]=='span'or tag[0]== 'cc' or tag[0]=='bb':
hide_char=get_position_xy(html,tag[1],tag[0],url)
result.append(hide_char)
#防止被其它標簽干擾
elif tag[0]==''or tag[0]=='p'or tag[0]=='div':
continue
#遇到中文及不含標簽則直接加入list
else:
result.append(tag)
return ''.join(result).strip('\n').strip()
if __name__=='__main__':
url ='https://www.dianping.com/shop/507576/review_all'
cookies={}#自己加入當時訪問的cookies
headers = {'User-Agent': ''}#加入自己的User-Agent
response=requests.get(url,headers=headers,cookies=cookies)
html = lxml.html.fromstring(response.content)
#找到標簽數(shù)據(jù)人弓,必須要保留標簽,這里以商店地址為例
rule = re.compile('<div class="address-info">(.*?)</div>', re.S)
address=re.findall(rule,response.text)[0]
address = address.strip().replace('\n','').replace(' ','')
#找到css表着逐,css表鏈接存放位置固定崔赌,所以直接獲取
css = html.xpath('//link[@rel="stylesheet"]/@href')[1]
url ='http:'+css.strip()
result = get_hide_string(address,url)
result=result.replace(' ','')
print('解密前數(shù)據(jù):'+address)
print('解密后數(shù)據(jù):'+result)
代碼運行結果 如圖4-1所示:
六、總結
總結一下耸别,就是先找到被替換數(shù)據(jù)標簽健芭,發(fā)現(xiàn)替換時對應的css表,找到css表秀姐,根據(jù)標簽屬性class值作key值去找到對應的坐標慈迈,同時找到標簽類別所對應的svg數(shù)據(jù)表鏈接,最后參照數(shù)據(jù)表坐標得到被隱藏的數(shù)據(jù)省有。
大眾點評前端JS加密方法與平時遇到的都不太一樣痒留,所以花了一些時間來講,之后遇到不一樣的JS加密也會給大家一起學習探討蠢沿,同樣如果發(fā)現(xiàn)文章的不足伸头,歡迎指出。