自學了一段時間的爬蟲, 感受吧..., 反反爬蟲纔是最重要的. 所謂的反反爬蟲就是針對反爬蟲作出應對的策略, 從而照樣爬取數據. 但前提是要了解反爬蟲的策略.javascript
部分能夠參考一篇關於反爬基礎的知乎文章第四章:爬蟲和反爬蟲之間的較量html
小插曲. 在知乎上看到一個提問的回答, 寫道爬蟲的等級:java
需求: 經過爬蟲實現中譯英翻譯
所需模塊:python
pip install requests pip install PyExecJs
PyExecJs
是一個使用 python 執行 js 代碼的模塊. 像這樣的模塊還有 js2py
(直接執行js 代碼), phantomjs
(無界面瀏覽器, 已經中止更新, 可能會出bug, 不推薦使用), selenium
(自動化工具) 等.c++
打開百度翻譯網站就發現發現它的 url 是: https://fanyi.baidu.com/?aldtype=16047#auto/zh
因此不難看出這是一個 get 請求的 url, 應該能夠在開發者工具中找到 ?aldtype=16047
文件, 以下圖示:ajax
全部的get 請求相關的信息, 均可以在這裏獲取了.json
而 get 確定不是咱們真正要找的 url, 在線翻譯的網站, 通常都會經過 post 請求實現實時翻譯. 那麼, 咱們就須要在開發者工具中繼續找有 post 請求的文件.
不當心發現一個叫 verify?token=
什麼什麼的文件是一個 post 請求的, 但狀態碼 Status Code
不是請求錯誤(403), 就是重定向(302). 因此不是咱們要找的.
遇到這樣的問題, 通常狀況下先提交一下翻譯的內容, 而後再去抓包工具裏(開發者工具)看, 發現一個叫 v2transapi
的文件是咱們所要尋找的.
因此咱們能夠在這裏索取咱們想要的post
信息, 真正要找的post
請求url是: https://fanyi.baidu.com/v2transapi.
然而, 啪啪啪…, 寫完代碼以後, 解析獲得的信息是含有 error
的. 說明有問題.api
因而乎, 在開發者工具中, 怒懟ctrl+shift+f
鍵, 在search 輸入框中輸入 v2transapi
, 接着懟回車, 雙擊查找到的結果, 好像發現了一些端倪.
裏面包藏着亂七八糟的 js 代碼, 有點暈, 還不知道究竟是不是所要找的內容, 但這是目前惟一掌握的線索了, 仍是要堅持看一看.
果不其然, 看到了熟悉又陌生的內容 $.ajax({type:"POST",url:"/v2transapi",cache:!1,data:p})
.
據分析(ps: 若是不想一步步往下分析, 那麼能夠在那一行打個斷點), 裏面的 data 對應的內容 p, 是咱們的下一個線索.
既然它可使用 p 變量, 那麼 p 確定是已經被賦值了的, 那麼網上一找應該就不難發現 p: p={from:g.fromLang,to:g.toLang,query:a,transtype:n,simple_means_flag:3,sign:m(a),token:window.common.token};
有些似曾相識啊. 據分析, 這個 p 正是咱們的 form data
, 各個參數都同樣, 以下圖所示:瀏覽器
這麼多數據, 哪個纔是咱們最須要關心的呢? 天然是sign
, 由於它貌似使用了 m 函數來加密了, 並且在以前的分析步驟中, 能夠發現它是一個可變數據, 其餘的參數是不變的,能夠直接從抓包工具上獲取.
因此咱們要進一步去分析 sign
.
在 $.ajax({type:"POST",url:"/v2transapi",cache:!1,data:p})
處打個斷點, 而後再翻譯的輸入框中, 隨便輸入要翻譯的內容, 這時候進入了調試的狀態.
接着尋找剛纔找到的 m 函數之處, 鼠標移動到 m 之上, 發現彈出的是這樣的內容, 以下圖:微信
說明 m 指向的是與 e 函數相關的, 接着咱們把 e 函數相關的內容拷貝下來, 存到一個.js 文件中, 而後使用 execjs 來執行 js 代碼便可.
若是對Chrome調試工具不太瞭解的, 或者想作谷歌翻譯爬蟲的, 能夠參考這篇文章爬蟲技巧:逆向破解js代碼加密,代碼混淆不是難事.
import json import re import execjs import js2py import requests class baidu_translate(): def __init__(self): self.GET_URL = 'https://fanyi.baidu.com/?aldtype=16047#zh/en/' self.POST_URL = 'https://fanyi.baidu.com/v2transapi' self.GET_HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0', 'Accept': '*/*', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Host': 'fanyi.baidu.com', 'Origin': 'https://fanyi.baidu.com', 'Referer': 'https://fanyi.baidu.com/', 'X-Requested-With': 'XMLHttpRequest', # 'cookie': '', } self.POST_HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0', 'Accept': '*/*', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Host': 'fanyi.baidu.com', 'Origin': 'https://fanyi.baidu.com', 'Referer': 'https://fanyi.baidu.com/?aldtype=16047', 'X-Requested-With': 'XMLHttpRequest', } self._session = requests.session() self._data = { 'from': 'zh', 'to': 'en', 'transtype': 'realtime', 'simple_means_flag': '3', # 'query': '', # 要翻譯的詞或句, 變項 # 'sign': '', # Ajax(js) 加密, 變項 # 'token': '', # 不變項 } # 能夠直接拿瀏覽器上的 token, 由於 token 是不變的 self._get_token() def _get_token(self): response = self._session.get(self.GET_URL, headers=self.GET_HEADERS) html = response.text li = re.search(r"<script>\s*window\[\'common\'\] = ([\s\S]*?)</script>", html) token = re.search(r"token: \'([a-zA-Z0-9]+)\',", li.group(1)) self._data['token'] = token.group(1) def _get_sign(self): # 將 使用 js 加密的函數 copy 到 baidu_translate.js 文件中 # 而後使用 execjs 執行 with open('baidu_translate.js') as fp: sign = execjs.compile(fp.read()).call('e', self._query) self._data['sign'] = sign def translate(self, query): self._query = query self._get_sign() self._data['query'] = self._query print(self._data) response = self._session.post(self.POST_URL, data=self._data, headers=self.POST_HEADERS) content = response.content.decode() dict_data = json.loads(content) # print(dict_data) ret = dict_data['trans_result']['data'][0]['dst'] print(ret) if __name__ == "__main__": trans = baidu_translate() trans.translate('百度翻譯, 我在爬你哦')
var i = "320305.131321201" function a(r){if(Array.isArray(r)){for(var o=0,t=Array(r.length);o<r.length;o++)t[o]=r[o]; return t}return Array.from(r)} function n(r,o){for(var t=0;t<o.length-2;t+=3){var a=o.charAt(t+2);a=a>="a"?a.charCodeAt(0)-87:Number(a),a="+"===o.charAt(t+1)?r>>>a:r<<a,r="+"===o.charAt(t)?r+a&4294967295:r^a }return r} function e(r) { var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); if (null === o) { var t = r.length; t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr( - 10, 10)) } else { for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++)"" !== e[C] && f.push.apply(f, a(e[C].split(""))), C !== h - 1 && f.push(o[C]); var g = f.length; g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice( - 10).join("")) } var u = void 0, l = "" + String.fromCharCode(103) + String.fromCharCode(116) + String.fromCharCode(107); u = null !== i ? i: (i = window[l] || "") || ""; for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { var A = r.charCodeAt(v); 128 > A ? S[c++] = A: (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), S[c++] = A >> 18 | 240, S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, S[c++] = A >> 6 & 63 | 128), S[c++] = 63 & A | 128) } for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), b = 0; b < S.length; b++) p += S[b], p = n(p, F); return p = n(p, D), p ^= s, 0 > p && (p = (2147483647 & p) + 2147483648), p %= 1e6, p.toString() + "." + (p ^ m) }