使用python/casperjs編寫終極爬蟲-客戶端App的抓取

發表於2015-08-31

1.緣起

隨著移動網際網路的發展,現在寫web和我三年前剛開始寫爬蟲的時候已經改變了太多。特別是在node以及javascript/ruby社群的努力下,以往“伺服器端”做的事情都慢慢搬到了“瀏覽器”來實現,最極端的例子可能是meteor了 ,寫web程式無需劃分前端後端的時代已經到來了。。。

在這一方面,Google一向是最激進的。縱觀Google目前的產品線,社交的Google Plus,網站分析的Google Analytics,Google目前賴以生存的Google Adwords等,如果想下載原始碼,用ElementTree來解析網頁,那什麼都得不到,因為Google的資料都是通過Ajax呼叫經過資料混淆處理的資料,然後用JavaScript進行解析渲染到頁面上的。

本來這種事情也不算太多,忍一忍就行了,不過最近因業務需要,經常需要上Google的Keyword Tools來分析特定關鍵字的搜尋量。

圖為關鍵字搜尋的截圖

圖為Google經過混淆處理的Ajax返回結果。

要把這麼費勁的事情自動化倒也不難,因為Google也提供了API來做,Adwords專案的TargetingIdeaService就是來做這個的,問題是Google的API呼叫需要花錢,而如果能用爬蟲技術來爬取這個結果,就能省去不必要的額外開銷。

2. Selenium WebDriver

由於要解析執行復雜的JavaScript,必須有一個Full Stack的瀏覽器JavaScript環境,這種環境三年前的話,可能只能訴諸於於selenium,selenium是一款多語言的瀏覽器Driver,它最大的優點在於,提供了從命令列統一操控多種不同瀏覽器的方法,這極大地方便了web產品的相容性自動化測試。

2.1 在沒有圖形介面的伺服器上安裝和使用Selenium

安裝selenium非常簡單,pip install selenium 即可,但是要讓firefox/chrome工作,沒有圖形介面的話,還是要費一番功夫的。

推薦的做法是

Selenium的安裝和配置在此就不多說了,值得注意的是,如果是Ubuntu使用者,並且要使用Chrome的話,必須額外下載一個chromedriver,並且把安裝的chromium-browser連結到/usr/bin/google-chrome,否則將無法執行。

2.2 爬取Keywords

先總結一下Adwords的使用方法吧,要能正常使用Adwords,必須要有一個開通Adwords的Google Account,這倒不是很難,只要訪問 http://adwords.google.com ,Google會協助建立賬號,如果還沒有的話,其次就是登陸了。

通過分析登陸頁面,我們可以看到需要在id為Email的一個input框內輸入email,然後在id為Passwd的密碼框內輸入密碼,然後點選Sign in提交這個頁面內唯一的form。

首先別忘了開一個瀏覽器先

登陸後,我們發現需要訪問一個類似 https://adwords.google.com/o/Targeting/Explorer 的網頁才能跳轉到關鍵字工具,於是我們手動生成一下這個網頁

到了工具主頁以後,事情就變得Tricky起來了。因為整個關鍵字工具都是個客戶端App,在全部檔案載入完成以後,頁面不會直接渲染完畢,而是要經過複雜的JavaScript運算後頁面才會完整顯示。然而Selenium WebDriver並不知道這一點,所以我們要讓他知道。

在這裡,我們要等待Search按鈕在瀏覽器中出現以後,才能確認網頁載入完畢,Selenium WebDriver有兩種方式可以實現這一點,我偷懶用了全域性的預設等待機制:

於是Selenium就會在找不到頁面元素的時候自動等候不超過30秒

接下來,等待輸入框和Search按鈕出現後提交搜尋iphone關鍵字的請求

然後我們繼續等待class為sLNB的table的出現,並解析結果

這裡我們使用了xpath來提取網頁特徵,這也算是寫爬蟲的必備吧。

完整的例子見: https://gist.github.com/3798896 替換email和passwd後直接就能用了

3. JavaScript Headless解決方案

隨著Node以及隨之而來的JavaScript社群的進化,如今的我們就幸福多了。遠的我們有phantomjs, 一個Headless的WebKit Driver,意味著可以無需GUI,完全模擬Chrome/Safari的操作。 近的有casperjs(基於phantomjs的好用封裝),zombie(相比phantomjs的優勢是可以和node整合)等。

其中非常可惜地是,zombiejs似乎對富JavaScript網站支援得有問題,所以後來我還是隻能用casperjs來進行測試。Headless的方案因為不需要渲染GUI,執行速度約為Selenium方案的三倍。

另外由於這是純JavaScript的方案,於是我們可以直接在例如Chrome的Console模式下寫程式碼控制瀏覽器,不存在如Selenium那樣還需要語義轉換,非常簡潔直觀。例如利用W3C Selectors API Level 1所提供的querySelector來快速選取元素,對錶單進行submit,對按鈕進行click,甚至可以執行自定義JavaScript指令碼以便按一定規律對頁面進行操控。

但是casperjs或者說phantomjs的弱點是不支援除了檔案讀寫和瀏覽器操作以外的一切*nix IPC慣用伎倆,socket神馬的統統不支援,1.4版本以後才加入了一個webserver用於和外界通訊,但是用httpserver來和外界通訊?我有點牴觸就是了。

廢話不說了,casperjs的程式碼看起來就是這樣,登陸:

與Selenium類似,因為頁面都是Ajax呼叫的,我們需要明確地“等待某個元素出現”,即:waitForSelector,casperjs的文件既簡潔又漂亮,不妨多逛逛。

值得一提的是,casperjs一定要呼叫casper.run方法,之前的start, then等方法,只是把步驟封裝到了this._steps裡面,只有在run的時候才會真正執行,所以casperjs設計流程的時候會很痛苦,for/each之類的手法有時並不好用。

這個時候需要用JavaScript程式設計比較常用的遞迴化的方法,參見https://github.com/n1k0/casperjs/blob/master/samples/dynamic.js 這個例子。我在完整的casperjs程式碼裡面也是這麼做的。

具體邏輯的實現和selenium類似,我就不廢話了,完整的例子參見: https://gist.github.com/3798922

4. 綜上

介紹了selenium和casperjs兩種不同的終極爬蟲寫法,但是其實這篇文寫來只是太久沒更新了,寫點東西更新一下而已:)

相關文章