本文利用WebCollector內(nèi)核的解析,來描述如何設(shè)計一個網(wǎng)絡(luò)爬蟲。我們先來看看兩個非常優(yōu)秀爬蟲的設(shè)計。
Nutch由apache開源組織提供,主頁:http://nutch.apache.org/
Nutch是目前最好的網(wǎng)絡(luò)爬蟲之一,Nutch分為內(nèi)核和插件兩個模塊組成,內(nèi)核控制整個爬取的邏輯,插件負(fù)責(zé)完成每個細(xì)節(jié)(與流程無關(guān)的細(xì)節(jié))的實現(xiàn)。具體分工如下:
內(nèi)核:控制爬蟲按照 Inject -> Generator -> Fetch -> Parse -> Updatedb ( -> 提交索引(可選))的流程進(jìn)行,而且這些流程都是利用map reduce在hadoop上實現(xiàn)的。
插件:實現(xiàn)爬蟲的http請求、解析器、URL過濾器、索引等細(xì)節(jié)功能。
Nutch的內(nèi)核提供了穩(wěn)定的可在集群上運行的爬取機制(廣度遍歷),插件為爬蟲提供了強大的擴(kuò)展能力。
Crawler4j由Yasser Ganjisaffar(微軟bing的一位工程師)提供,項目主頁:https://code.google.com/p/crawler4j/
用Crawler4j寫爬蟲,用戶只需要指定兩處:
1) 爬蟲的種子、線程數(shù)等配置
2)覆蓋WebCrawler類的visit(Page page)方法,對每個頁面的自定義操作(抽取、存儲)
Nutch是被設(shè)計在hadoop上的,而且插件的調(diào)度以反射的形式實現(xiàn),所以它的插件機制,并不如想象的那樣靈活。寫一個插件需要附帶幾個配置文件,并修改Nutch總配置文件。而且Nutch其實是為了搜索引擎定制的,所以NUTCH提供的掛載點,并不能做精抽取之類的業(yè)務(wù)提供很好的擴(kuò)展。
Crwler4j雖然提供精簡的用戶接口,但是并沒有一套插件機制,來定制自己的爬蟲。例如用Crawler4j來爬取新浪微博,就需要修改源碼,來完成對新浪微博的模擬登陸。
主頁:https://github.com/CrawlScript/WebCollector
WebCollector使用了Nutch的爬取邏輯(分層廣度遍歷),Crawler4j的的用戶接口(覆蓋visit方法,定義用戶操作),以及一套自己的插件機制,設(shè)計了一套爬蟲內(nèi)核。
WebCollector內(nèi)核構(gòu)架圖:
CrawlDB: 任務(wù)數(shù)據(jù)庫,爬蟲的爬取任務(wù)(類似URL列表)是存放在CrawlDB中的,CrawlDB根據(jù)DbUpdater和Generator所選插件不同,可以有多種形式,如文件、redis、mysql、mongodb等。
Injector: 種子注入器,負(fù)責(zé)第一輪爬取時,向CrawlDB中提交爬取任務(wù)。在斷點續(xù)爬的時候,不需要通過Injector向CrawlDB注入種子,因為CrawlDB中已有爬取任務(wù)。
Generator: 任務(wù)生成器,任務(wù)生成器從CrawlDB獲取爬取任務(wù),并進(jìn)行過濾(正則、爬取間隔等),將任務(wù)提交給抓取器。
Fetcher: 抓取器,F(xiàn)etcher是爬蟲最核心的模塊,F(xiàn)etcher負(fù)責(zé)從Generator中獲取爬取任務(wù),用線程池來執(zhí)行爬取任務(wù),并對爬取的網(wǎng)頁進(jìn)行鏈接解析,將鏈接信息更新到CrawlDB中,作為下一輪的爬取任務(wù)。在網(wǎng)頁被爬取成功/失敗的時候,F(xiàn)etcher會將網(wǎng)頁和相關(guān)信息以消息的形式,發(fā)送到Handler的用戶自定義模塊,讓用戶自己處理網(wǎng)頁內(nèi)容(抽取、存儲)。
DbUpdater: 任務(wù)更新器,用來更新任務(wù)的狀態(tài)和加入新的任務(wù),網(wǎng)頁爬取成功后需要更新CrawlDB中的狀態(tài),對網(wǎng)頁做解析,發(fā)現(xiàn)新的連接,也需要更新CrawlDB。
Handler: 消息發(fā)送/處理器,F(xiàn)etcher利用Handler把網(wǎng)頁信息打包,發(fā)送到用戶自定義操作模塊。
User Defined Operation: 用戶自定義的對網(wǎng)頁信息進(jìn)行處理的模塊,例如網(wǎng)頁抽取、存儲。爬蟲二次開發(fā)主要就是自定義User Defined Operation這個模塊。實際上User Defined Operation也是在Handler里定義的。
RequestFactory: Http請求生成器,通過RequestFactory來選擇不同的插件,來生成Http請求,例如可以通過httpclient插件來使用httpclient作為爬蟲的http請求,或者來使用可模擬登陸新浪微博的插件,來發(fā)送爬取新浪微博的http請求。
ParserFactory: 用來選擇不同的鏈接分析器(插件)。爬蟲之所以可以從一個網(wǎng)頁開始,向多個網(wǎng)頁不斷地爬取,就是因為它在不斷的解析已知網(wǎng)頁中的鏈接,來發(fā)現(xiàn)新的未知網(wǎng)頁,然后對新的網(wǎng)頁進(jìn)行同樣的操作。
爬取邏輯:
WebCollector和Nutch一樣,把爬蟲的廣度遍歷拆分成了分層的操作。
第一層:爬取一個網(wǎng)頁,http://www.apache.org/,解析網(wǎng)頁,獲取3個鏈接,將3個鏈接保存到CrawlDB中,設(shè)置狀態(tài)為未爬取。同時將http://www.apache.org/的爬取狀態(tài)設(shè)置為已爬取。結(jié)束第一輪。
第二層,找到CrawlDB中狀態(tài)為未爬取的頁面(第一層解析出來的3個鏈接),分別爬取,并解析網(wǎng)頁,一共獲得8個鏈接。和第一層操作一樣,將解析出的鏈接放入CrawlDB,設(shè)置為未爬取,并將第二層爬取的三個頁面,狀態(tài)設(shè)置為已爬取。
第三層,找到CrawlDB中狀態(tài)為未爬取的頁面(第二層解析出來的8個鏈接).................
每一層都可以作為一個獨立的任務(wù)去運行,所以可以將一個大型的廣度遍歷任務(wù),拆分成一個一個小任務(wù)。爬蟲里有個參數(shù),設(shè)置爬取的層數(shù),指的就是這個。
插件機制:
框架圖中的 Injector、Generator、Request(由RequestFactory生成)、Parser(由ParserFactory生成)、DbUpdater、Response都是以插件實現(xiàn)的。制作插件往往只需要自定義一個實現(xiàn)相關(guān)接口的類,并在相關(guān)Factory內(nèi)指定即可。
WebCollector內(nèi)置了一套插件(cn.edu.hfut.dmic.webcollector.plugin.redis)?;谶@套插件,可以把WebCollector的任務(wù)管理放到redis數(shù)據(jù)庫上,這使得WebCollector可以爬取海量的數(shù)據(jù)(上億級別)。
用戶自定義操作:
對于用戶來說,關(guān)注的更多的不是爬蟲的爬取流程,而是對每個網(wǎng)頁要進(jìn)行什么樣的操作。對網(wǎng)頁進(jìn)行抽取、保存還是其他操作,應(yīng)該是由用戶自定義的。
假設(shè)我們有個需求,要爬取《知乎》上的所有提問。對用戶來說,只需要定義對知乎的提問如何抽取。
覆蓋BreadthCrawler類的visit方法,即可實現(xiàn)用戶自定義操作,完全不用考慮爬蟲的爬取邏輯。
WebCollector的設(shè)計主要來自于Nutch,相當(dāng)于將Nutch抽象成了一個爬蟲內(nèi)核。
最后再次附上項目地址:https://github.com/CrawlScript/WebCollector