前言
我們在寫爬蟲的時候經常會遇到各種反爬措施,比如現在各種大型網站越來越多的js加載令人十分頭疼。
這些網站的數據不像簡單的網站一樣可以直接拿取,我們經常會找不到數據源頭,難道只能使用selenium來模擬瀏覽器拿取嗎?當然不是的。
本文就以如何破解有道翻譯的參數為例來一步步完成js的破解。
網頁分析
首先打開chrome調試台,隨便在目標網址種輸入一句話讓它翻譯過來,看一下請求的構造。(一般查看Network下的XHR選項,這是用於與服務器交互數據)

看到一個名字為translate開頭的文件,猜測這就是我們要找的東西,接下來點開這個文件看看返回的響應是什么

沒錯,我們可以看出來這返回的json數據就是我們要的數據,包含了需要翻譯的語句與翻譯后的語句,接下來我們來分析這個請求

我們可以得到這個請求的網址,同時可以看出來請求是post方式,那么它post了哪些數據呢?我們繼續往下看。

接下來我們先使用下requests來構造下請求,看看能否直接構造成功。
import requests url='http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule' headers={ "Accept":"application/json, text/javascript, */*; q=0.01", "Accept-Encoding":"gzip, deflate", "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8", "Connection":"keep-alive", "Content-Length":"251", "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", "Cookie":"OUTFOX_SEARCH_USER_ID=-198948014@10.169.0.84; OUTFOX_SEARCH_USER_ID_NCOO=1678156251.6498268; _ga=GA1.2.109926435.1582269589; UM_distinctid=170a4e98abc891-0b24cc8bee43ca-5e4f281b-144000-170a4e98abd72b; JSESSIONID=aaaPbNnhvgdpJe62qgofx; ___rl__test__cookies=1586153715407", "Host":"fanyi.youdao.com", "Origin":"http://fanyi.youdao.com", "Referer":"http://fanyi.youdao.com/", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", "X-Requested-With":"XMLHttpRequest", } data={ "i":"你好", "from":"AUTO", "to":"AUTO", "smartresult":"dict", "client":"fanyideskweb", "salt":"15861537154130", "sign":"d0a1387da482d8d9dae92e0f24b6e4e0", "ts":"1586153715413", "bv":"5d4cb17cceb9ecd02ece3ed9923d3a7a", "doctype":"json", "version":"2.1", "keyfrom":"fanyi.web", "action":"FY_BY_REALTlME", } res=requests.post(url=url,headers=headers,data=data) print(res.text)
看一下運行結果
{"translateResult":[[{"tgt":"hello","src":"你好"}]],"errorCode":0,"type":"zh-CHS2en"}
看起來沒毛病啊,難道真的這么簡單嗎?其實不然,當我們把我們需要翻譯的數據從 你好 變成其他的語句時候就會發現出問題了,網頁會返回錯誤代碼。
所以我們需要分析到底什么地方出了問題,因為這是post方式,我們猜測是data數據種某些參數發生了變化,我們再次回到瀏覽器輸入其他語句看一下會有什么變化。

我們通過對比上一次翻譯的請求就會發現,post的data數據中有幾個參數好像每次都不一樣,那么這些參數是如何構造出來的呢?
打開調試台搜索如參數 sign 操作如下。

我們發現在某個js文件中有這個參數,猜測此文件就是構造參數的文件,打開這個文件(雙擊紫色框圈選部分即可)看看代碼。

代碼只顯示了一行好奇怪,沒關系,我們點擊紅色框的按鈕即可格式化代碼。

發現這是一個接近一萬行的js代碼,但是我們只需要構造4個參數呀,在這接近一萬行的代碼中怎么分析呢?我們繼續在代碼中搜索參數名稱試一試。
鼠標點擊一下代碼部分,然后鍵盤按ctrl-f即會出現代碼部分的搜索框。搜索結果大概十幾個的樣子,我們多觀察下搜索結果就會找到有用的函數。
如下圖中的函數,怎么樣,一看竟然出現了 ts、bv、salt、sign 四個參數,這不正是我們要找的構造函數嗎?

我們把這一段函數拿出來放進notepad++中分析一下

所以目前看起來我們只需要搞清楚變量 e與變量 t 中的 navigator.appVersion 是什么就可以搞定構造函數了。
其實現在已經成功一半了,我們已經找到了參數的構造方法,不過接下來的任務也不簡單,我們需要搞清楚兩個變量的內容是什么。
我們使用chrome調試台的debug功能來調試一下看看這兩個變量是什么,首先在關鍵代碼打上斷點(在對應代碼前點下鼠標出現藍色標識即可)。

接下來我們再次點擊左邊頁面中的翻譯按鈕讓頁面執行一次,看看變量會是什么(注意有可能需要點擊debug的下一步讓頁面繼續執行才可以看到參數)。
有兩種方法查看變量如下圖,一種是將鼠標移動到變量上即可,還有一種是在調試台打上變量名稱即可。

可以看出變量e是我們需要翻譯的語句,同理我們看下navigator.appVersion ,哦吼原來是user-agent。

現在我們已經搞清楚了所有的問題,那么接下來就是見證奇跡的時刻,我們將js的函數使用python構造出來,看下是否可以得到我們想要的參數結果。
import hashlib#用於md5加密 import time import random def md5_b(str): hash=hashlib.md5() hash.update(str.encode('utf-8')) return hash.hexdigest() data={} def get_data_params(t_str): user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" e=t_str t=md5_b(user_agent) r=str(int(time.time()*1000))#毫秒所以*1000 i=r+str(random.randint(0,10)) data['ts'] = r data['bv'] = t data['salt'] = i data['sign'] = md5_b("fanyideskweb" + e + i + "Nw(nmmbP%A-r6U3EUn]Aj") get_data_params('大家好') print(data)
這就是我們的復刻版構造函數了,我們看下結果是什么。
{'ts': '1586159044056', 'bv': '58ecb303644f080054fa932285a108a5', 'salt': '15861590440560', 'sign': 'dfac0f829d89a3f31ccb25a0eb16fd51'}
嗯,看起來挺像,那么我們接下來把其他不變的參數也添加進去,看看能否構造請求成功,接下來是完整代碼。
import requests import hashlib#用於md5加密 import time import random url='http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule' headers={ "Accept":"application/json, text/javascript, */*; q=0.01", "Accept-Encoding":"gzip, deflate", "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8", "Connection":"keep-alive", "Content-Length":"251", "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", "Cookie":"OUTFOX_SEARCH_USER_ID=-198948014@10.169.0.84; OUTFOX_SEARCH_USER_ID_NCOO=1678156251.6498268; _ga=GA1.2.109926435.1582269589; UM_distinctid=170a4e98abc891-0b24cc8bee43ca-5e4f281b-144000-170a4e98abd72b; JSESSIONID=aaaPbNnhvgdpJe62qgofx; ___rl__test__cookies=1586153715407", "Host":"fanyi.youdao.com", "Origin":"http://fanyi.youdao.com", "Referer":"http://fanyi.youdao.com/", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", "X-Requested-With":"XMLHttpRequest", } #data中的from與to參數代表着從什么語言翻譯到什么語言,默認是中英互譯 data={ "from":"AUTO", "to":"AUTO", "smartresult":"dict", "client":"fanyideskweb", "doctype":"json", "version":"2.1", "keyfrom":"fanyi.web", "action":"FY_BY_REALTlME", } #該函數用於字符串的md5加密 def md5_b(str): hash=hashlib.md5() hash.update(str.encode('utf-8')) return hash.hexdigest() def get_data_params(t_str): user_agent="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" e=t_str t=md5_b(user_agent) r=str(int(time.time()*1000))#毫秒所以*1000 i=r+str(random.randint(0,10)) data['i'] = e data['ts'] = r data['bv'] = t data['salt'] = i data['sign'] = md5_b("fanyideskweb" + e + i + "Nw(nmmbP%A-r6U3EUn]Aj") return data res=requests.post(url=url,headers=headers,data=get_data_params('你好啊,你在干什么呀?')) print(res.text)
看下運行結果:
{"translateResult":[[{"tgt":"Hello. What are you doing?","src":"你好啊,你在干什么呀?"}]],"errorCode":0,"type":"zh-CHS2en"}
好了,發現已經可以得到返回的json了,可以自己使用json庫繼續進行提取了,這樣我們就破解了有道翻譯的js加密,其他網站也類似這樣操作就可以。
