反思本身一年前作數據採集的通過——網絡爬蟲

之前沒有寫過,這是第一次寫,用詞不當,表述不清楚的地方請見諒。但願你們多提建議,謝謝。html

網絡爬蟲經常被人所忽略,特別是和搜索引擎的光環相比,它彷佛有些暗淡無光。我不多看見有詳細介紹爬蟲實現的文章或者文檔。然而,爬蟲實際上是很是重要的一個系統,特別是在今天這個數據爲王的時代。若是你是一個剛剛開始的公司或者項目,沒有任何原始的數據積累,那麼經過爬蟲去Internet上找到那些有價值的數據再進行數據的清洗和整理,是一個能夠快速獲得數據的重要手段。web

本文側重於爬蟲的系統設計和實現的部分細節,內容來源於三方面,一是我2013年3月份到10月份的一個數據採集的項目,對數據的要求是普通PC單機日下載量很多於80萬個有效頁面,第二則是來源於大量的網絡資料做參考,由於當時的公司的保密性不是很高,咱們在開發的過程當中遇到問題隨時能夠求助網絡,然而我使用最多的是Google搜索和  http://stackoverflow.com/  網站是我去的最多的地方,第三則是最近看的一本在華爲公司的角落裏存放好久的一本書《數學之美》。大部分關於爬蟲的系統方面的文獻都是2000年左右的,此後寥寥無幾,說明關於爬蟲的系統設計在十幾年前已經基本解決了。此外,既然本文側重於系統方面的問題,那麼某些內容就不會涉及,好比如何抓取那些隱藏的web數據,如何抓取ajax的頁面,如何動態調整抓取頻率等等。ajax

正文數據庫

一個正規的,完整的網絡爬蟲實際上是一個很複雜的系統:首先,它是一個海量數據處理系統,由於它所要面對的是整個互聯網的網頁,即使是一個小型的,垂直類的爬蟲,通常也須要抓取上十億或者上百億的網頁;其次,它也是一個對性能要求很好的系統,可能須要同時下載成千上萬的網頁,快速的提取網頁中的url,對海量的url進行去重,等等;最後,它確實是一個不面向終端用戶的系統,因此,雖然也很須要穩定性,但偶然的當機並不會是災難,並且,不會出現相似訪問量激增這樣的狀況,同時,若是短暫的時間內出現性能的下滑也不算是個問題,從這一點來看,爬蟲的系統設計在某些部分又變得簡單了許多。網頁爬蟲

 

上圖是一個爬蟲的系統框架,它基本上包括了一個爬蟲系統所須要的全部模塊。緩存

任何一個爬蟲系統的設計圖,會發現都有一個環路,這個環表明着爬蟲大體的工做流程:根據URL將對應的網頁下載下來,而後提取出網頁中包含的URL,再根據這些新的URL下載對應的網頁,周而復始。爬蟲系統的子模塊都位於這個環路中,並完成某項特定的功能。服務器

這些子模塊通常包括:網絡

Fetcher:用於根據url下載對應的網頁;架構

DNS Resolver:DNS的解析;框架

Content Seen:網頁內容的去重;

Extractor:提取網頁中的url或者其它的一些內容;

URL Filter:過濾掉不須要下載的url;

URL Seen:url的去重;

URL Set:存儲全部的url;

URL Frontier:調度器,決定接下來哪些下載哪些url對應的網頁;

FetcherDNS Resolver

這兩個模塊是兩個很是簡單的獨立的服務:DNS Resolver負責域名的解析;Fetcher的輸入是域名解析後的url,返回的則是該url對應的網頁內容。對於任何一次網頁的抓取,它都須要調用這兩個模塊。

對通常的爬蟲,兩個模塊能夠作得很是的簡單,甚至合併到一塊兒。可是對於性能要求很高的系統,它們可能成爲潛在的性能瓶頸。主要緣由是不管是域名解析仍是抓取,都是很耗時的工做。好比抓取網頁,通常的延遲都在百毫秒級別,若是趕上慢的網站,可能要幾秒甚至十幾秒,這致使工做線程會長時間的處於阻塞等待的狀態。若是但願Fetcher可以達到每秒幾千個網頁甚至更高的下載,就須要啓動大量的工做線程。

所以,對於性能要求高的爬蟲系統,通常會採用epoll或者相似的技術將兩個模塊改爲異步機制。另外,對於DNS的解析結果也會緩存下來,大大下降了DNS解析的操做。

Content Seen

Internet上的一些站點經常存在着鏡像網站(mirror),即兩個網站的內容同樣但網頁對應的域名不一樣。這樣會致使對同一份網頁爬蟲重複抓取屢次。爲了不這種狀況,對於每一份抓取到的網頁,它首先須要進入Content Seen模塊。該模塊會判斷網頁的內容是否和已下載過的某個網頁的內容一致,若是一致,則該網頁不會再被送去進行下一步的處理。這樣的作法可以顯著的下降爬蟲須要下載的網頁數。

至於若是判斷兩個網頁的內容是否一致,通常的思路是這樣的:並不會去直接比較兩個網頁的內容,而是將網頁的內容通過計算生成FingerPrint(信息指紋),一般FingerPrint是一個固定長度的字符串,要比網頁的正文短不少。若是兩個網頁的FingerPrint同樣,則認爲它們內容徹底相同。

ExtractorUrl Filter

Extractor的工做是從下載的網頁中將它包含的全部url提取出來。這是個細緻的工做,你須要考慮到全部可能的url的樣式,好比網頁中經常會包含相對路徑的url,提取的時候須要將它轉換成絕對路徑。

Url Filter則是對提取出來的url再進行一次篩選。不一樣的應用篩選的標準是不同的,好比對於baidu/google的搜索,通常不進行篩選,可是對於垂直搜索或者定向抓取的應用,那麼它可能只須要知足某個條件的url,好比不須要圖片的url,好比只須要某個特定網站的url等等。Url Filter是一個和應用密切相關的模塊

Url Seen

Url Seen用來作url去重。關於url去重以後會介紹,這裏就再也不詳談了。

對於一個大的爬蟲系統,它可能已經有百億或者千億的url,新來一個url如何能快速的判斷url是否已經出現過很是關鍵。由於大的爬蟲系統可能一秒鐘就會下載幾千個網頁,一個網頁通常可以抽取出幾十個url,而每一個url都須要執行去重操做,可想每秒須要執行大量的去重操做。所以Url Seen是整個爬蟲系統中很是有技術含量的一個部分。(Content Seen其實也存在這個問題)

Url Set

當url通過前面的一系列處理後就會被放入到Url Set中等待被調度抓取。由於url的數量很大,因此只有一小部分可能被放在內存中,而大部分則會寫入到硬盤。通常Url Set的實現就是一些文件或者是數據庫。

URL Frontier

URL Frontier之因此放在最後,是由於它能夠說是整個爬蟲系統的引擎和驅動,組織和調用其它的模塊。

當爬蟲啓動的時候,Froniter內部會有一些種子url,它先將種子url送入Fetcher進行抓取,而後將抓取下來的網頁送入Extractor提取新的url,再將新的url去重後放入到Url Set中;而當Froniter內部的url都已經抓取完畢後,它又從Url Set中提取那些新的沒有被抓取過的url,周而復始。Frontier的調度實現有不少種,這裏只介紹最多見的一種實現方法。

在此以前,須要先解釋一點,儘管在介紹Fetcher的時候咱們說,好的Fetcher每秒可以下載百千個網頁,可是對於某個特定的目標網站,好比www.sina.com,爬蟲系統對它的抓取是很是慢速的,十幾秒纔會抓取一次,這是爲了保證目標網站不至於被爬蟲給抓垮。

爲了作到這一點,Frontier內部對於每一個域名有一個對應的FIFO隊列,這個隊列保存了該域名下的url。Frontier每次都會從某個隊列中拿出一個url進行抓取。隊列會保存上一次被Frontier調用的時間,若是該時間距離如今已經超過了必定值,那麼該隊列才能夠再次被調用。

Frontier內部同時可能擁有成千上萬個這樣的隊列,它會輪詢的獲取一個能夠被調用的隊列,而後從該隊列中pull一個url進行抓取。而一旦全部隊列中的url被消耗到必定程度,Frontier又會從Url Set中提取一批新的url放入對應的隊列。

分佈式

當單機版的爬蟲性能不能知足要求的時候,就應該考慮用多臺機器組成分佈式的爬蟲系統。分佈式的爬蟲架構其實要比想象的簡單得多,一個樸素的作法是:假設有N臺機器,每臺機器上有運行了一個完整的爬蟲系統,每臺機器的爬蟲在從Extractor模塊得到新的url以後,根據url的域名進行hash而後取模N獲得結果n,而後該url會被放入第n臺機器的Url Set中。這樣,不一樣網站的url會被放在不一樣的機器上處理。

當時咱們設計之處的目標是把爬蟲程序放到路由器這樣的設備上也能夠正常、穩定的運行,由於不須要再設備上存放沒用的信息,全部的有用頁面經過socket通訊被存放到指定的服務器中。但當我離開公司的時候,這個方案已經沒有人再提到了,由於咱們發現這樣的作法,咱們沒有硬件支持,並且影響了路由器的運行速度。

以上就是一個完整爬蟲的系統實現。固然,因爲篇幅有限迴避了一些細節。好比爬蟲抓取每一個網站前須要先讀取該網站的robots.txt來判斷該網站是否容許被抓取(關於robots.txt的一下解釋能夠參考http://www.cnblogs.com/yuzhongwusan/archive/2008/12/06/1348969.html);再好比,一些網站提供了sitemap,這樣能夠直接從sitemap上獲取該網站的全部url;等等。還有一張來自網絡的抓取平臺的結構圖,這個圖的結構和上面的那張圖的結構基本相同。

相關文章
相關標籤/搜索