本來打算昨晚發的,結果園子又遷移......
網絡爬蟲(Spider或Crawler),顧名思義,就是在互聯網上爬行的蟲子,那么這只蟲子為什么要在網上爬行呢?很簡單:收集信息。在互聯網時代,誰掌握了信息誰就把握了主動權。曾經我一直覺得做搜索的公司都是慈善家,他們自己花錢為大眾服務,真是太高尚了,直到我知道谷歌每年大半的盈利來自廣告,我才明白那句名言——互聯網上最昂貴的東西就是免費,因為它能讓你輕易的接受,卻無法舍棄。(我想多數人離開了搜索引擎,將在網絡上寸步難行)
好吧,扯多了,我們先看下下圖。我們可以很容易的看出,網絡爬蟲的根本任務就是從互聯網抓取數據,存入數據庫或本地文件系統以供使用。
從圖中看來似乎一個網絡爬蟲的功能很簡單,但是可不要小看這一個方框所包含的內容,它里邊的很多步驟展開之后都包含着一方面技術(當然,菜鳥我就不怎么展開說了,水平不足,怕出丑),下圖是一個普通爬蟲的內部實現圖:
如圖所示,往往一個完整的怕成包含兩大部分:調度部分和作業部分。其中調度部分可以看做一個總控制器(准確的說通常總控線程包含調度線程),它完成爬蟲的啟動初始化(配置線程數,Url集合大小,加載Url種子、當前抓取進度、已抓取集合,以及其他配置信息)、運行時調度(為作業線程分配資源)、善后處理(當url集合為空時采取的策略等),有時候還要負責與外部系統交互(如分布式的情況下,一個爬蟲將作為一個作業,而調度線程則需要和總控服務器交互信息)。而作業線程就是苦逼干活的了,完成的任務如圖所示。
從圖中我們可以提出以下幾個問題(都是粗淺的,但是對於實現一個爬蟲卻是基本的):
1、如何從互聯網請求數據
2、作業線程的怎樣實現?啟動多少個合適?也調度線程如何交互?
3、如何處理抽取出來的Url(爬取策略)?
4、Url如何濾重?
下邊我們就一一來討論:
1、如何從互聯網請求數據?
在討論這個問題之前我們先來思考一個問題:為什么我們輸入url之后一按回車,瀏覽器就能將對應的頁面顯示出來?多數時候瀏覽器根據url向服務器發起請求(一般情況下使用Http協議,不懂的建議看看帥氣的肖佳大大的系列文章:http://www.cnblogs.com/TankXiao/category/415412.html 。),將對應頁面下載到本地,然后瀏覽器的解釋器將頁面源碼解釋為我們看到的圖文元素,如下圖。
從上邊的過程我們可以想到,我們只需要模擬瀏覽器想服務器發起請求即可。對於Java而言,這在多數情況下很容易實現的,Java本身提供了實現請求的對象,如HttpURLConnection,而且使用Apache的子項目HttpClient也能方便實現這些功能(當然熟悉Socket更好,一些情況下直接使用socket來實現不僅更靈活,而且能夠在性能上有很大突破)。至於請求的具體實現,網上已有大量資料,但是提醒一點的是,針對對當前爬蟲泛濫的情況,不少網站都在反爬蟲上做了功夫(往往程序猿們愛爬的網站不想被別人爬,而且程序猿們的小爬蟲通常不會遵守啥Robots.txt協議且極具破壞力,曾經就爬癱過小網站,在此深感抱歉,希望貴網站內換個給力點的服務器以供菜鳥練手......),所以熟悉http等請求協議會使你事半功倍,至少搞定一些普通的請求沒問題,我們要堅信互聯網一條原則:所見必可得。
最后建議想研究這方面的菜鳥同胞們,學會使用chrome/firefix的Debugger和一些抓包工具(如fiddler2、Sniffer)是很有必要的。
2、作業線程的怎樣實現?啟動多少個合適?也調度線程如何交互?
想要實現一個良好的爬蟲(甚至說一個可用的),必然要采用到多線程技術,這也是用java實現一個優秀系統必然設計的(很多時候你不知不覺就已經用到了)。至於實現的細節在此不再贅述。對於一個小型爬蟲而言,我覺得應該至少實現三部分:調度、工作和日志(之所以強調日志是因為其對於我們掌控爬蟲的狀態是很有用的,一個不可控的程序聽着就讓人不爽)。其中調度和日志一般一個線程計可,而工作線程一般需要配置多個,這里建議小型爬蟲使用java的線程池機制即可(為啥JobSearch里沒有?額.....因為當時我還不會),可以節省很大功夫。
那么一般我們應該啟動多少個工作線程呢?我也不知道。這是個很難回答的問題,我們知道對於軟件系統的性能最終會匯集到一個詞:IO(磁盤IO和網絡IO),而這有和硬件密切相關的。對已一個穩健性良好的程序,在PC和機房的小型機上跑,性能差距往往很明顯。那么這個問題應該如何解決呢?這里就要提到優秀爬蟲應該做到的一項原則:可配置度應該高。也就是說我們應該將想要經常改變的內容變為可配置的,這點在很多開源軟件中很常見。如此一來我們可以將工作線程池的大小在配置文件中定義,在部署之后根據服務器的運行情況來調整即可。
至於工作線程與調度線程如何交互,其實就是通過待抓取Url集合(通常是隊列)和已抓取Url集合(用以濾重,有其他更好的替代方案,之后會講到)。調度線程一般維護全局的Url抓取隊列,而工作線程每次從中取出一條或幾條進行抓取。這里需特別注意的就是線程間的資源競爭(Url),所以Url隊列需特別保護(例如同步變量或者使用ThreadLocal)。
好吧,寫的速度太慢了,倆小時才寫了這么多。今天就先講到這了,后兩個問題改天補上吧。
最后共享一篇收藏的文章,里邊講的比我這菜鳥級的強多了。另外最經看了hadoop的一些初級的基本原理,感覺其很多實現思想對我深有啟發,大家沒事也可以了解下。
共享文檔(版權屬其原作者所有):搜索引擎系統學習與開發總結