文/高玉民 翟浩然
當今世界,互聯(lián)網(wǎng)飛速發(fā)展,數(shù)據(jù)量呈指數(shù)級高速增長,企業(yè)和個人對于快速獲得有效信息的需求也越來越強烈。在數(shù)據(jù)采集方面,網(wǎng)絡爬蟲系統(tǒng)的應用,特別是基于Node.js技術(shù)的分布式爬蟲系統(tǒng)的應用,大大提升了數(shù)據(jù)采集的廣度和深度。
本系統(tǒng)是基于Node.js技術(shù)、采用消息隊列通信的可配置的分布式爬蟲系統(tǒng)。系統(tǒng)采用主從架構(gòu)、系統(tǒng)耦合度較低、可靈活配置多項任務,也可根據(jù)需要快速部署新爬蟲,系統(tǒng)可擴展性較強。
系統(tǒng)整體架構(gòu)采用Master-worker方式,即由Master節(jié)點監(jiān)控系統(tǒng)運行,接收用戶控制,存儲最終數(shù)據(jù);Worker節(jié)點接受任務運行,完成網(wǎng)頁下載和信息抽取工作。
如圖1所示,每一個爬蟲系統(tǒng)有且只有1個Master節(jié)點,該系統(tǒng)下Worker節(jié)點的數(shù)量受到消息隊列效率、數(shù)據(jù)抓取和存儲效率的限制,理論上可以有無限多個。Master和Worker節(jié)點采用消息隊列的方式進行通信,Worker節(jié)點之間互不通信。
同時,每一個爬蟲系統(tǒng)需要配置一個Redis實例;不同爬蟲系統(tǒng)之間可以共享一個Mongodb數(shù)據(jù)庫,且最好使用一個Mongodb實例,以提高系統(tǒng)的運行效率。
每一個爬蟲系統(tǒng)由一個Master節(jié)點統(tǒng)一進行管理和控制,Master節(jié)點主要功能包括監(jiān)控各節(jié)點工作狀態(tài),接收、分配和管理爬蟲任務,數(shù)據(jù)存儲等。
系統(tǒng)啟動時,Master節(jié)點會依次啟動自身的Http服務、數(shù)據(jù)庫連接和消息隊列連接。完成消息隊列連接后,系統(tǒng)會收到項下所有Worker節(jié)點的上線信息,并將這些節(jié)點的相關(guān)信息存儲在自身Worker列表中,以方便任務調(diào)用。
Master和Worker之間使用消息隊列進行通信,通信消息格式如表1所示。
本系統(tǒng)的消息隊列通過Redis推送/訂閱模塊實現(xiàn),若選用其他消息隊列系統(tǒng),可重新編寫通信格式。
每當有新的Worker節(jié)點上線,Master節(jié)點都會收到并記錄該Worker節(jié)點的信息,并通過分配任務和任務回調(diào),判斷節(jié)點當前是否空閑。若節(jié)點掉線,Master會將該Worker移除Worker列表。用戶可通過Master提供的網(wǎng)絡API查詢當前所有節(jié)點的信息和運行狀態(tài),但不可指定Worker節(jié)點任務分配,不可指定Worker結(jié)束運行。
若Master節(jié)點掉線,其所屬所有Worker節(jié)點都將記錄當前工作狀態(tài),正在執(zhí)行任務的節(jié)點會停止當前工作,等待Master重新上線。
Master節(jié)點提供一系列的網(wǎng)絡API方便用戶提交任務并監(jiān)控任務狀態(tài)。
2.3.1 任務提交
在系統(tǒng)上線后,用戶可通過Master節(jié)點提供的網(wǎng)絡API上傳爬取任務。每個任務都需要填寫統(tǒng)一的任務信息,以便爬蟲能夠識別任務、過濾URL和網(wǎng)頁元素、合成最終數(shù)據(jù)。
任務數(shù)據(jù)包含的內(nèi)容具體如表2所示。
用戶填寫好上述信息后,封裝成Json格式,向/New_task發(fā)起Post請求;若任務被成功接收,系統(tǒng)會返回成功信息和任務當前狀態(tài)。
2.3.2 任務運行和狀態(tài)
Master獲得任務后,會通過下屬Worker節(jié)點的信息來分配任務。一般來說,Master總是選擇第一個空閑Worker來分配任務;若沒有空閑Worker節(jié)點,則接收到的任務進入任務等待隊列。
分配好的任務會將自身狀態(tài)設置為“運行中”,并記錄負責該任務的Worker節(jié)點信息。用戶可通過查詢?nèi)蝿諄慝@取Worker信息,然后通過查看Worker當前狀態(tài)來獲取該任務的進度。
用戶可以隨時取消等待隊列中的任務,取消后,該任務狀態(tài)設置為“已取消”,不再被任何Worker運行。用戶也可以取消正在運行中的任務,即,通過取消負責該任務的Worker當前工作來完成這一動作;取消正在進行的任務時,Worker會等待該任務當前最后一個URL抓取成功后再取消任務的運行,避免異常情況的發(fā)生;任務取消后,Worker會通知Master任務已經(jīng)取消,Master會將該Worker節(jié)點置為空閑狀態(tài)。
Worker在完成任務后,會向Master節(jié)點返回任務完成信息;Master節(jié)點將該Worker狀態(tài)置為空閑。
在任務完成或進行中任務被取消后,Master節(jié)點向任務等待隊列詢問是否有等待任務;若發(fā)現(xiàn)等待任務,則進入新的任務分配和運行環(huán)節(jié)。如圖2所示。
2.3.3 定時任務
對于需要定時爬取的任務,不必通過網(wǎng)絡API指定,僅需要將其配置在Master啟動任務文件中,由Master節(jié)點按時調(diào)用。具體任務的創(chuàng)建、分配及管理過程如上文所示。
表1
圖1:系統(tǒng)整體架構(gòu)圖
圖2
圖3
每當Worker節(jié)點抓取到一條符合要求的數(shù)據(jù)后,都會將抓取后的數(shù)據(jù)按照一定的格式返回Master,由Master節(jié)點負責數(shù)據(jù)存儲。由于系統(tǒng)運行速度和數(shù)據(jù)格式的不斷變化,本系統(tǒng)數(shù)據(jù)存儲采用Mongodb數(shù)據(jù)庫。數(shù)據(jù)存儲格式如表3所示。
用戶可根據(jù)爬取任務域和主題從本數(shù)據(jù)庫中提取數(shù)據(jù),以便進行深層次的分析和應用。
Worker節(jié)點負責從Master接受任務、爬取信息,并將用戶所需的結(jié)果返回至Master。Worker節(jié)點由任務調(diào)度器、網(wǎng)頁下載器、內(nèi)容抽取器組成。
Worker啟動時,會自動啟動一個Redis客戶端,用于和Master進行消息通信。同時,檢查自身是否存在未完成任務,避免異常退出重啟導致任務丟失。完成前述動作后,Worker會根據(jù)配置好的信息源爬取代理IP信息,并存入任務調(diào)度模塊中。
啟動完成后,Worker會向Master發(fā)送注冊請求,將自身信息注冊到Master上,具體通信格式請參照上文相關(guān)內(nèi)容。若Worker發(fā)現(xiàn)存在未完成的任務,則直接進行任務中余下的網(wǎng)頁爬取,并在注冊時提交自身“非空閑”的信息。
若Master節(jié)點掉線,在工作中的Worker節(jié)點收到通知后會立即暫停當前任務,等待Master節(jié)點上線后重新啟動任務。
任務調(diào)度器負責跟蹤任務URL列表,根據(jù)爬取記錄添加新的URL、刪除已經(jīng)抓取過的URL。任務調(diào)度器會直接丟棄新爬取的網(wǎng)頁鏈接中已經(jīng)抓取過的URL。在分配網(wǎng)頁抓取任務時,從代理IP列表中隨機抽取一個,供網(wǎng)頁下載器使用。
網(wǎng)頁抓取完成后,任務調(diào)度器負責處理抓取信息,從中提取URL所在網(wǎng)站的其他未抓取的URL,完成計數(shù)、統(tǒng)計代理IP是否失效、將處理好的結(jié)果發(fā)送給Master節(jié)點的相關(guān)工作??傮w來說,任務調(diào)度器可以看作是任務和爬取結(jié)果的一個中間層設置。
為靈活應對不同情形,本系統(tǒng)采用三種方式進行網(wǎng)頁下載,具體如下:
(1)當網(wǎng)頁中不存在反爬取功能時,系統(tǒng)使用Request模塊進行網(wǎng)頁請求和內(nèi)容獲?。?/p>
(2)當網(wǎng)頁中存在反爬取功能,需要渲染出整個網(wǎng)頁或在其中需要模擬鼠標鍵盤事件的,系統(tǒng)會通過進程間通信,使用Phantomjs軟件進行渲染和網(wǎng)頁內(nèi)容獲?。?/p>
(3)當信息源提供公開的網(wǎng)絡API時,可通過網(wǎng)絡API繞過內(nèi)容抽取器直接獲取對應信息。
以上三種方法均支持通過代理IP向目標服務通信。其中,在使用Phantomjs時,支持調(diào)用Phantomjs腳本,以便更好地模擬真人試用網(wǎng)頁的情況。
表2
表3
內(nèi)容抽取器是Worker節(jié)點的最核心模塊,負責將下載好的網(wǎng)頁信息按照用戶配置的方法從DOM元素中提取相應的字段、搜集URL目標網(wǎng)站包含的其他鏈接,并按照用戶規(guī)定的模式,從相應DOM中獲取數(shù)據(jù),按照標題組合形成Object實體;最后將抽取組合好的信息返回給任務調(diào)度器,由任務調(diào)度器作出最終處理,返回給Master。
如圖3所示。
需求,隨時增加或減少Worker節(jié)點,提升系統(tǒng)的運營效率。
(2)本系統(tǒng)支持用戶根據(jù)網(wǎng)頁特性和數(shù)據(jù)分布,自定義信息提取的DOM標記,并分配給每個信息項目一個標題,自動完成面向數(shù)據(jù)的規(guī)范化過程,大幅度減少數(shù)據(jù)應用和分析的ETL時間,從源頭上加快信息收集的效率。即,一切任務以用戶所想的方式進行,一切數(shù)據(jù)以用戶所需的形式處理。
(3)本系統(tǒng)采用Mongodb作為數(shù)據(jù)存儲的工具,其Schema-free的特征保證了在完全兼容多變的數(shù)據(jù)結(jié)構(gòu)的情況下,最大程度地保持數(shù)據(jù)的規(guī)范性,并能夠在快速運行時兼顧多Worker同時工作場景下的數(shù)據(jù)存儲要求。
(4)本系統(tǒng)通過代理訪問目標源,防止目標源根據(jù)請求發(fā)起IP限制訪問;本系統(tǒng)規(guī)定了每個IP訪問目標源的最小時間間隔,不會對目標服務進行過度請求,保證了目標源的運營安全;同時,對于采取了輕度反爬措施的網(wǎng)站,本系統(tǒng)也有相應的處理方法,增強了系統(tǒng)的可用性。
(1)本系統(tǒng)基于Node.js技術(shù)開發(fā),與其他技術(shù)框架相比,能夠更好地處理由Javascript渲染的網(wǎng)頁信息、支持Json這種應用最廣泛的網(wǎng)絡數(shù)據(jù)格式。
同時,由于Javascript本身的特性,本系統(tǒng)在保持與Python開發(fā)效率相當?shù)那闆r下,實現(xiàn)更快速度的運行。此外,Node.js框架與Java技術(shù)在網(wǎng)頁內(nèi)容抓取的效率相當,但該框架在IO方面的性能更加高效,占用的機器資源更少,有利于降低開發(fā)和使用成本。
本系統(tǒng)使用PM2模塊進行部署和管理,同時,由于采用了分布式架構(gòu),可以根據(jù)業(yè)務