爬蟲解析網頁資料時,最棘手的問題莫過於關鍵資料被加密,被混淆。加大瞭解析難度,常見的諸如登陸密碼,token等被混淆成了一個長長的字串。好在這些加密都是javascript在瀏覽器中進行,找到這些js程式碼並破解並不是難事。
谷歌的Chrome瀏覽器有個開發工具(DevTools),可以幫助前端開發者完成除錯JavaScript程式碼等工作。這個工具非常棒,也是爬蟲開發者的有力工具,對於研究複雜網站(通過大量的js混淆、加密資料及其訪問介面)的爬取很有幫助。
今天,我們就來講講,如何利用Chrome除錯JavaScript的功能來找到js加密相關的程式碼。
為了防止爬蟲,網站開發者們也是費盡了心思:
首先,他們使用JavaScript把一些資料介面通過多次請求、加密生成token等方式來生成資料介面的訪問URL;
然後,他們又把這些JavaScript壓縮、混淆使得它們晦澀難懂,或者分成很多不同的js檔案,讓你要找到某些關鍵的程式碼如同大海撈針。
最後,就把一部分爬蟲開發者拒之門外了。
不服輸的程式設計師,利用有力的工具,加上不服輸的精神就開始了鑽研之旅。
接下來,我們鑽研一下如何通過Chrome除錯js程式碼來找到Google中文翻譯的請求介面。
下面是https://translate.google.cn/ 的截圖,這個網站在國內可以無障礙訪問。
在左邊輸入“Python”,右邊立馬出現“蟒蛇”,而整個網頁並沒有重新整理,這是通過一個ajax請求實現的翻譯。
那麼問題來了:這個ajax請求的URL是什麼?
通過Chrome的開發工具(按F12調出)的“Networks”可以看到,請求的是:
https://translate.google.cn/translate_a/single?client=webapp&sl=auto&tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&otf=1&ssel=0&tsel=0&kc=1&tk=606016.1025355&q=Python
這個URL裡面有個引數: tk=606016.1025355,觀察發現,這個引數的值每次請求既然還不一樣!
這個引數是如何生成的呢?為了能使用這ajax翻譯介面,我們得需要好好鑽研一番。
第一步:觀察網頁載入了哪些js檔案,猜猜哪個檔案可能包含tk生成的程式碼。
在Networks裡面看看都載入了哪些js檔案,可以通過點選 Filter 右邊的“js”按鈕只顯示js檔案。tips:按住Ctrl(非Mac)或Command(Mac)來選擇多個過濾器。
觀察它們的名字,我猜是“translate_m_zh-CN.js”。哈哈,猜對了!別問問是怎麼猜的,就是看名字憑感覺。
開啟這個js一看,誒呀媽呀,密密麻麻一大片,完全沒法讀!!!(多虧不能讀,如果能清晰的讀起來,就沒有後面的故事了)
第二步:瞭解“Sources”工具
切換到開發工具的“Sources”,可以看到如下視窗:
(1)上部左邊的“Page”是各種資原始檔的路徑,js就在這裡找。
(2)上部右邊是顯示程式碼的視窗,可以開啟多個程式碼檔案,每個檔案一個標籤頁,標籤頁標題顯示檔名稱。當開啟一個被壓縮過的js檔案時,它會提醒“Pretty-print this minified file?”(要格式化程式碼嗎?),點選後面的“more”可以看到如何操作,也就是點選內碼表下邊的花括號“{}”即可。可以點選程式碼左邊的行號設定斷點。
(3)下部左邊是除錯視窗,比較重要的有:
XHR/fetch Breakpoints : 在請求某個URL時設為斷點;
Call Stack: 執行到斷點時,函式呼叫的棧,從上到下按離斷點近遠排序;
(4)下部右邊是執行到斷點時,“Scope”顯示區域性變數和全域性變數,“Watch”可以新增要觀察的變數。
上面這4部分的佈局會隨著整個devtools視窗的寬窄而變化,把devtools視窗拉寬,它們可能會排成一排。
第三步:除錯JavaScript,探尋關鍵程式碼
我們的目的是要找到“tk”這個變數的生成方法,從而可以使用ajax翻譯介面。前面我們已經探討出,tk是ajax介面(即前面列的那個包含“/translate_a/single”的URL),那我們就設定斷點,在訪問這個URL前停止。具體做法如下:
(1)設定XHR/fetch Breakpoints斷點,點選它後面的加號“+”,把這個URL新增上,問號後面的引數就不用填了。
(2)在Google翻譯的輸入框隨便輸入個單詞,比如“Python”,然後網頁就變灰(鎖死),上方出現“Paused in debugger”,如下圖:
這時候,我們觀察程式目前的狀態:
(a)原始碼那裡,停止在”this.xa.send(a)”;
(b)“Call Stack”顯示了呼叫關係,最上面的就是原始碼那裡的send函式。
小技巧:把devtools拉寬,讓Call Stack和程式碼兩個視窗並列,更容易檢視。
從上往下依次點選Call Stack的每一行,每次點選都會顯示對應的程式碼片段。當點選到第三個時發現似乎找到了答案:
(注意:上面截圖的行數和變數名稱有可能和你看到的不一樣)
棕色背景列出了變數的當前值,可以看到8332行的b包含了tk引數,而這個引數明顯是來自c,再往上到8329行找到了c來自函式Uo(),滑鼠放到Uo上面停一會兒,就可以跳到它的定義處,也就是8323行,同樣滑鼠懸停在$n()函式並跳到它的定義:
觀察這個函式,這是一段演算法,請求返回的字串就是tk值那樣的形如“a.b”的一串數字,那麼它就是我們要找的tk值的生成演算法。至此,我們就通過Chrome除錯JavaScript找到了js中我們想要的程式碼。
要弄明白這段演算法,你就要懂得JavaScript語言,及時不會js如果你會Python,在搜尋引擎的幫助下也可以很快弄明白它並用Python來實現。
再來看看這段程式碼,它是如何讓人“看不懂”它的,也就是下面的“不好好寫程式大法”,掌握此法有助於你破解它,撥開迷霧見真相。
擴充套件:如何不好好寫程式
(1)String.fromCharCode()
這個函式就是Python的chr(),把數字變成字母。看7504行,函式Xn的引數是個字母,不直接寫偏偏要通過函式轉化一下。真有一種脫褲子放屁的趕腳。
(2)閒著沒事兒就寫個函式
在看那個函式Xn的實現:
真是閒的蛋疼!
(3)eval函式
除了上面兩個方法,eval也常用。這個JavaScript的eval和Python的eval一樣。
寫爬蟲避免不了和複雜的網頁(網站)打交道,它們以各種方法“防”,爬蟲以各種方法“攻”。攻防之間可以說是道高一尺魔高一丈,來回較量。本文通過Chrome除錯JavaScript是一種“攻”的方法,還可以使用抓包工具(比如 Charles)進行更復雜的分析,Charles提供了更豐富的搜尋功能等。這兩種方法基本上算是一種策略,另外比較省事的就是使用Selenium的Chrome driver,開發起來簡單很多,但執行效率會低一些,適合抓取頻次低(有些封得你沒辦法頻次高)的網站。
爬蟲文章延伸閱讀:
為爬蟲獲取登入cookies: 使用Charles和requests模擬微博登入
網路爬蟲小偏方:突破登入和訪問頻率限制,多研究對方不同終端產品
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***