首頁  >  文章  >  web前端  >  Node.js實作的簡易網頁抓取功能範例_node.js

Node.js實作的簡易網頁抓取功能範例_node.js

WBOY
WBOY原創
2016-05-16 16:28:491280瀏覽

現今,網頁抓取已經是一種人所共知的技術了,然而依然存在著諸多複雜性, 簡單的網頁爬蟲依然難以勝任Ajax輪訓、XMLHttpRequest,WebSockets,Flash Sockets等各種複雜技術所開發出來的現代化網站。

我們以我們在Hubdoc這個專案上的基礎需求為例,在這個專案中,我們從銀行,公共事業和信用卡公司的網站上抓取帳單金額,到期日期,帳戶號碼,以及最重要的:近期帳單的pdf。對於這個項目,我一開始採用了很簡單的方案(暫時並沒有使用我們正在評估的昂貴的商業化產品)——我以前在MessageLab/Symantec使用Perl做過的一個簡單的爬蟲項目。但是結果很不順利,垃圾郵件發送者所製作的網站要比銀行和公共事業公司的網站簡單的多得多。

那麼如何解決這個問題呢?我們主要從使用Mikea開發的優秀 request函式庫開始。在瀏覽器中發出請求,並在Network視窗中查看到底發送出去了什麼請求頭,然後把這些請求頭拷貝到程式碼裡。這個過程很簡單。只是追蹤從登陸開始,到下載Pdf檔案結束的這個過程,然後模擬這個過程的所有的請求而已。為了讓類似的事情處理起來變得容易,並且能讓網頁開發者們更加合理地寫爬蟲程序,我把從HTML上取到結果的方把導出到jQuery中(使用輕量級cheerio函式庫),這使得相似的工作變得簡單,也讓利用CSS選擇子選取一個頁面中的元素變得較為簡單。整個過程被包裝進一個框架,而這個框架也可以做額外的工作,例如從資料庫中拾取證書,載入個體機器人,和UI透過socket.io溝通。

對於某些web網站來說這個是有效的,但這只是JS腳本,而不是我那個被這些公司放在他們網站上的node.js的code。他們對遺留下來的問題,針對複雜性就行分層,使得你非常難去弄清楚該做什麼來得到登入的資訊點。對於一些網站我嘗試了幾天透過與request()庫結合來獲取,但仍是徒勞無功。

在幾乎崩潰後,我發現了node-phantomjs,這個庫可以讓我從node中控制phantomjs headless webkit瀏覽器(譯者註:這個我沒想到一個對應的名詞,headless這裡的意思是渲染頁面在後台完成,無需顯示設備)。這看起來是個簡單的解決方案,但還有一些phantomjs無法迴避的問題需要解決:

1.PhantomJS只能告訴你頁面是否完成了加載,但是你無法確定這個過程中是否存在透過JavaScript或meta標籤實現的重定向(redirect)。特別是JavaScript使用setTimeout()來延遲呼叫的時候。

2.PhantomJS為你提供了一個頁面加載開始(pageLoadStarted)的鉤子,允許你處理上面提到的問題,但是這個機能只能在你確定要加載的頁面數,在每個頁面加載完成時減少這個數字,並且為可能的超時提供處理(因為這種事情並不總是會發生),這樣當你的數字減少為0,就可以調用你的回調函數了。這種方式可以運作,但是總是讓人覺得有點像是駭客手段。

3.PhantomJS每抓取一個頁面需要一個完整獨立的進程,因為如果不這樣,無法分離每個頁面之間的cookies。如果你是用同一個phantomjs進程,已經登入的頁面中的session會被送到另一個頁面中。

4.無法使用PhantomJS下載資源 - 你只能將頁面儲存為png或pdf。這很有用,但這意味著我們需要求助於request()來下載pdf。

5.由於上述的原因,我必須找到一個方法來將cookie從PhantomJS的session中分發到request()的session庫中去。只需要將document.cookie的字串分發過去,解析它,然後將其註入到request()的cookie jar中去。

6.將變數注入到瀏覽器session中並不是件容易的事。要這麼做我需要建立一個字串來建立一個Javascript函數。

複製程式碼 程式碼如下:

Robot.prototype.add_page_data = function (page, name, data) {
 page.evaluate(
 "function () { var " name " = window." name " = " JSON.stringify(data) "}"
 );
}

7.有些網站總是充斥著console.log()之類的程式碼,也需要將他們重新定義,輸出到我們希望的位置。為了完成這個,我這麼做:
複製程式碼 程式碼如下:

if (!console.log) {
    var iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    console = window.frames[0].console;
}

8.有些網站總是充斥著console.log()之類的程式碼,也需要將他們重新定義,輸出到我們希望的位置。為了完成這個,我這麼做:

複製程式碼 程式碼如下:

if (!console.log) {
    var iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    console = window.frames[0].console;
}

9.告訴瀏覽器我點選了a標籤也是件很不容易的事情,為了完成這些事情,我加入了以下的程式碼:
複製程式碼 程式碼如下:

var clickElement = window.clickElement = function (id){
    var a = document.getElementById(id);
    var e = document.createEvent("MouseEvents");
    e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    a.dispatchEvent(e);
 };

10.我還需要限制瀏覽器session的最大並發量,從而保障我們不會爆掉伺服器。雖然這麼說,但這個限制比昂貴的商業解決方案所能提供的高很多。 (譯者註:即商業解決方案的併發量比這個解決方案大)

所有的工作結束後,我就有一個比較體面的 PhantomJS request 的爬蟲解決方案。必須使用 PhantomJS 登入後才可以返回去 request() 請求,它將使用在 PhantomJS 中設定的 Cookie 來驗證登入的會話。這是一個巨大的勝利,因為我們可以使用 request() 的串流來下載 pdf檔。

整個的計劃是為了讓 Web 開發者相對容易的理解如何使用 jQuery 和 CSS 選擇器來創建不同 Web 網站的爬蟲,我還沒有成功證明這個思路可行,但相信很快會了。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn