網頁爬蟲及其用到的算法和數據結構



網絡爬蟲程序的優劣,很大程度上反映了一個搜索引擎的好差。不信,你能夠隨便拿一個網站去查詢一下各家搜索對它的網頁收錄狀況,爬蟲強大程度跟搜索引擎好壞基本成正比。

1.世界上最簡單的爬蟲——三行情詩

咱們先來看一個最簡單的最簡單的爬蟲,用python寫成,只須要三行。
import requests url="http://www.cricode.com" r=requests.get(url)
上面這三行爬蟲程序,就以下面這三行情詩通常,很乾脆利落。

是好男人,

就應該在和女朋友吵架時,

抱着必輸的心態。
2.一個正常的爬蟲程序

上面那個最簡單的爬蟲,是一個不完整的殘疾的爬蟲。由於爬蟲程序一般須要作的事情以下:
  • 1)給定的種子URLs,爬蟲程序將全部種子URL頁面爬取下來
  • 2)爬蟲程序解析爬取到的URL頁面中的連接,將這些連接放入待爬取URL集合中
  • 3)重複一、2步,直到達到指定條件才結束爬取
所以,一個完整的爬蟲大概是這樣子的:
import requests                       #用來爬取網頁 
from bs4 import BeautifulSoup         #用來解析網頁 
seds = ["http://www.hao123.com",      #咱們的種子               
        "http://www.csdn.net",              
         http://www.cricode.com] 
sum = 0                               #咱們設定終止條件爲:爬取到100000個頁面時,就不玩了   
while sum < 10000 :     
    if sum < len(seds):          
        r = requests.get(seds[sum])          
        sum = sum + 1          
        do_save_action(r)         
        soup = BeautifulSoup(r.content)                         
        urls = soup.find_all("href",.....)                     //解析網頁          
        for url in urls:               
            seds.append(url)                   else:          
                    break
3.如今來找茬

上面那個完整的爬蟲,不足20行代碼,相信你能找出20個茬來。由於它的缺點實在是太多。下面一一列舉它的N宗罪:
  • 1)咱們的任務是爬取1萬個網頁,按上面這個程序,一我的在默默的爬取,假設爬起一個網頁3秒鐘,那麼,爬一萬個網頁須要3萬秒鐘。MGD,咱們應當考慮開啓多個線程(池)去一塊兒爬取,或者用分佈式架構去併發的爬取網頁。
  • 2)種子URL和後續解析到的URL都放在一個列表裏,咱們應該設計一個更合理的數據結構來存放這些待爬取的URL纔是,好比隊列或者優先隊列。
  • 3)對各個網站的url,咱們一視同仁,事實上,咱們應當區別對待。大站好站優先原則應當予以考慮。
  • 4)每次發起請求,咱們都是根據url發起請求,而這個過程當中會牽涉到DNS解析,將url轉換成ip地址。一個網站一般由成千上萬的URL,所以,咱們能夠考慮將這些網站域名的IP地址進行緩存,避免每次都發起DNS請求,費時費力。
  • 5)解析到網頁中的urls後,咱們沒有作任何去重處理,所有放入待爬取的列表中。事實上,可能有不少連接是重複的,咱們作了不少重複勞動。
  • 6)…..
4.找了這麼多茬後,頗有成就感,真正的問題來了,學挖掘機到底哪家強?

如今咱們就來一一討論上面找茬找出的若干問題的解決方案。

1)並行爬起問題

咱們能夠有多重方法去實現並行。

多線程或者線程池方式,一個爬蟲程序內部開啓多個線程。同一臺機器開啓多個爬蟲程序,如此,咱們就有N多爬取線程在同時工做。能大大減小時間。

此外,當咱們要爬取的任務特別多時,一臺機器、一個網點確定是不夠的,咱們必須考慮分佈式爬蟲。常見的分佈式架構有:主從(Master——Slave)架構、點對點(Peer to Peer)架構,混合架構等。

說道分佈式架構,那咱們須要考慮的問題就有不少,咱們須要分派任務,各個爬蟲之間須要通訊合做,共同完成任務,不要重複爬取相同的網頁。分派任務咱們要作到公平公正,就須要考慮如何進行負載均衡。負載均衡,咱們第一個想到的就是Hash,好比根據網站域名進行hash。

負載均衡分派完任務以後,千萬不要覺得萬事大吉了,萬一哪臺機器掛了呢?原先指派給掛掉的哪臺機器的任務指派給誰?又或者哪天要增長几臺機器,任務有該如何進行從新分配呢?

一個比較好的解決方案是用一致性Hash算法。

2)待爬取網頁隊列

如何對待待抓取隊列,跟操做系統如何調度進程是相似的場景。

不一樣網站,重要程度不一樣,所以,能夠設計一個優先級隊列來存放待爬起的網頁連接。如此一來,每次抓取時,咱們都優先爬取重要的網頁。

固然,你也能夠效仿操做系統的進程調度策略之多級反饋隊列調度算法。

3)DNS緩存

爲了不每次都發起DNS查詢,咱們能夠將DNS進行緩存。DNS緩存固然是設計一個hash表來存儲已有的域名及其IP。

4)網頁去重

說到網頁去重,第一個想到的是垃圾郵件過濾。垃圾郵件過濾一個經典的解決方案是Bloom Filter(布隆過濾器)。布隆過濾器原理簡單來講就是:創建一個大的位數組,而後用多個Hash函數對同一個url進行hash獲得多個數字,而後將位數組中這些數字對應的位置爲1。下次再來一個url時,一樣是用多個Hash函數進行hash,獲得多個數字,咱們只須要判斷位數組中這些數字對應的爲是全爲1,若是全爲1,那麼說明這個url已經出現過。如此,便完成了url去重的問題。固然,這種方法會有偏差,只要偏差在咱們的容忍範圍之類,好比1萬個網頁,我只爬取到了9999個,剩下那一個網頁,who cares!

5)數據存儲的問題

數據存儲一樣是個頗有技術含量的問題。用關係數據庫存取仍是用NoSQL,抑或是本身設計特定的文件格式進行存儲,都大有文章可作。

6)進程間通訊

分佈式爬蟲,就必然離不開進程間的通訊。咱們能夠以規定的數據格式進行數據交互,完成進程間通訊。

7)……
 
廢話說了那麼多,真正的問題來了,問題不是學挖掘機到底哪家強?而是如何實現上面這些東西!:)

實現的過程當中,你會發現,咱們要考慮的問題遠遠不止上面這些。紙上得來終覺淺,覺知此事要躬行!
相關文章
相關標籤/搜索