文/高玉民 翟浩然
當今世界,互聯網飛速發展,數據量呈指數級高速增長,企業和個人對于快速獲得有效信息的需求也越來越強烈。在數據采集方面,網絡爬蟲系統的應用,特別是基于Node.js技術的分布式爬蟲系統的應用,大大提升了數據采集的廣度和深度。
本系統是基于Node.js技術、采用消息隊列通信的可配置的分布式爬蟲系統。系統采用主從架構、系統耦合度較低、可靈活配置多項任務,也可根據需要快速部署新爬蟲,系統可擴展性較強。
系統整體架構采用Master-worker方式,即由Master節點監控系統運行,接收用戶控制,存儲最終數據;Worker節點接受任務運行,完成網頁下載和信息抽取工作。
如圖1所示,每一個爬蟲系統有且只有1個Master節點,該系統下Worker節點的數量受到消息隊列效率、數據抓取和存儲效率的限制,理論上可以有無限多個。Master和Worker節點采用消息隊列的方式進行通信,Worker節點之間互不通信。
同時,每一個爬蟲系統需要配置一個Redis實例;不同爬蟲系統之間可以共享一個Mongodb數據庫,且最好使用一個Mongodb實例,以提高系統的運行效率。
每一個爬蟲系統由一個Master節點統一進行管理和控制,Master節點主要功能包括監控各節點工作狀態,接收、分配和管理爬蟲任務,數據存儲等。
系統啟動時,Master節點會依次啟動自身的Http服務、數據庫連接和消息隊列連接。完成消息隊列連接后,系統會收到項下所有Worker節點的上線信息,并將這些節點的相關信息存儲在自身Worker列表中,以方便任務調用。
Master和Worker之間使用消息隊列進行通信,通信消息格式如表1所示。
本系統的消息隊列通過Redis推送/訂閱模塊實現,若選用其他消息隊列系統,可重新編寫通信格式。
每當有新的Worker節點上線,Master節點都會收到并記錄該Worker節點的信息,并通過分配任務和任務回調,判斷節點當前是否空閑。若節點掉線,Master會將該Worker移除Worker列表。用戶可通過Master提供的網絡API查詢當前所有節點的信息和運行狀態,但不可指定Worker節點任務分配,不可指定Worker結束運行。
若Master節點掉線,其所屬所有Worker節點都將記錄當前工作狀態,正在執行任務的節點會停止當前工作,等待Master重新上線。
Master節點提供一系列的網絡API方便用戶提交任務并監控任務狀態。
2.3.1 任務提交
在系統上線后,用戶可通過Master節點提供的網絡API上傳爬取任務。每個任務都需要填寫統一的任務信息,以便爬蟲能夠識別任務、過濾URL和網頁元素、合成最終數據。
任務數據包含的內容具體如表2所示。
用戶填寫好上述信息后,封裝成Json格式,向/New_task發起Post請求;若任務被成功接收,系統會返回成功信息和任務當前狀態。
2.3.2 任務運行和狀態
Master獲得任務后,會通過下屬Worker節點的信息來分配任務。一般來說,Master總是選擇第一個空閑Worker來分配任務;若沒有空閑Worker節點,則接收到的任務進入任務等待隊列。
分配好的任務會將自身狀態設置為“運行中”,并記錄負責該任務的Worker節點信息。用戶可通過查詢任務來獲取Worker信息,然后通過查看Worker當前狀態來獲取該任務的進度。
用戶可以隨時取消等待隊列中的任務,取消后,該任務狀態設置為“已取消”,不再被任何Worker運行。用戶也可以取消正在運行中的任務,即,通過取消負責該任務的Worker當前工作來完成這一動作;取消正在進行的任務時,Worker會等待該任務當前最后一個URL抓取成功后再取消任務的運行,避免異常情況的發生;任務取消后,Worker會通知Master任務已經取消,Master會將該Worker節點置為空閑狀態。
Worker在完成任務后,會向Master節點返回任務完成信息;Master節點將該Worker狀態置為空閑。
在任務完成或進行中任務被取消后,Master節點向任務等待隊列詢問是否有等待任務;若發現等待任務,則進入新的任務分配和運行環節。如圖2所示。
2.3.3 定時任務
對于需要定時爬取的任務,不必通過網絡API指定,僅需要將其配置在Master啟動任務文件中,由Master節點按時調用。具體任務的創建、分配及管理過程如上文所示。

表1

圖1:系統整體架構圖

圖2

圖3
每當Worker節點抓取到一條符合要求的數據后,都會將抓取后的數據按照一定的格式返回Master,由Master節點負責數據存儲。由于系統運行速度和數據格式的不斷變化,本系統數據存儲采用Mongodb數據庫。數據存儲格式如表3所示。
用戶可根據爬取任務域和主題從本數據庫中提取數據,以便進行深層次的分析和應用。
Worker節點負責從Master接受任務、爬取信息,并將用戶所需的結果返回至Master。Worker節點由任務調度器、網頁下載器、內容抽取器組成。
Worker啟動時,會自動啟動一個Redis客戶端,用于和Master進行消息通信。同時,檢查自身是否存在未完成任務,避免異常退出重啟導致任務丟失。完成前述動作后,Worker會根據配置好的信息源爬取代理IP信息,并存入任務調度模塊中。
啟動完成后,Worker會向Master發送注冊請求,將自身信息注冊到Master上,具體通信格式請參照上文相關內容。若Worker發現存在未完成的任務,則直接進行任務中余下的網頁爬取,并在注冊時提交自身“非空閑”的信息。
若Master節點掉線,在工作中的Worker節點收到通知后會立即暫停當前任務,等待Master節點上線后重新啟動任務。
任務調度器負責跟蹤任務URL列表,根據爬取記錄添加新的URL、刪除已經抓取過的URL。任務調度器會直接丟棄新爬取的網頁鏈接中已經抓取過的URL。在分配網頁抓取任務時,從代理IP列表中隨機抽取一個,供網頁下載器使用。
網頁抓取完成后,任務調度器負責處理抓取信息,從中提取URL所在網站的其他未抓取的URL,完成計數、統計代理IP是否失效、將處理好的結果發送給Master節點的相關工作。總體來說,任務調度器可以看作是任務和爬取結果的一個中間層設置。
為靈活應對不同情形,本系統采用三種方式進行網頁下載,具體如下:
(1)當網頁中不存在反爬取功能時,系統使用Request模塊進行網頁請求和內容獲??;
(2)當網頁中存在反爬取功能,需要渲染出整個網頁或在其中需要模擬鼠標鍵盤事件的,系統會通過進程間通信,使用Phantomjs軟件進行渲染和網頁內容獲??;
(3)當信息源提供公開的網絡API時,可通過網絡API繞過內容抽取器直接獲取對應信息。
以上三種方法均支持通過代理IP向目標服務通信。其中,在使用Phantomjs時,支持調用Phantomjs腳本,以便更好地模擬真人試用網頁的情況。

表2

表3
內容抽取器是Worker節點的最核心模塊,負責將下載好的網頁信息按照用戶配置的方法從DOM元素中提取相應的字段、搜集URL目標網站包含的其他鏈接,并按照用戶規定的模式,從相應DOM中獲取數據,按照標題組合形成Object實體;最后將抽取組合好的信息返回給任務調度器,由任務調度器作出最終處理,返回給Master。
如圖3所示。
需求,隨時增加或減少Worker節點,提升系統的運營效率。
(2)本系統支持用戶根據網頁特性和數據分布,自定義信息提取的DOM標記,并分配給每個信息項目一個標題,自動完成面向數據的規范化過程,大幅度減少數據應用和分析的ETL時間,從源頭上加快信息收集的效率。即,一切任務以用戶所想的方式進行,一切數據以用戶所需的形式處理。
(3)本系統采用Mongodb作為數據存儲的工具,其Schema-free的特征保證了在完全兼容多變的數據結構的情況下,最大程度地保持數據的規范性,并能夠在快速運行時兼顧多Worker同時工作場景下的數據存儲要求。
(4)本系統通過代理訪問目標源,防止目標源根據請求發起IP限制訪問;本系統規定了每個IP訪問目標源的最小時間間隔,不會對目標服務進行過度請求,保證了目標源的運營安全;同時,對于采取了輕度反爬措施的網站,本系統也有相應的處理方法,增強了系統的可用性。
(1)本系統基于Node.js技術開發,與其他技術框架相比,能夠更好地處理由Javascript渲染的網頁信息、支持Json這種應用最廣泛的網絡數據格式。
同時,由于Javascript本身的特性,本系統在保持與Python開發效率相當的情況下,實現更快速度的運行。此外,Node.js框架與Java技術在網頁內容抓取的效率相當,但該框架在IO方面的性能更加高效,占用的機器資源更少,有利于降低開發和使用成本。
本系統使用PM2模塊進行部署和管理,同時,由于采用了分布式架構,可以根據業務