不管是產生新頁面,還是原本的頁面更新,這種變化都被稱為增量, 而爬取過程則被稱為增量爬取。那如何進行增量式的爬取工作呢?回想一下爬蟲的工作流程:
發送URL請求 ----- 獲得響應 ----- 解析內容 ----- 存儲內容
我們可以從幾種思路入手:
- 在發送請求之前判斷這個URL是不是之前爬取過
- 在解析內容后判斷這部分內容是不是之前爬取過
- 寫入存儲介質時判斷內容是不是已經在介質中存在
?
實現增量式爬取
不難發現,其實增量爬取的核心是去重, 至于去重的操作在哪個步驟起作用,只能說各有利弊,就像我說的,everything is tradeoff。
在我看來,前兩種思路需要根據實際情況取一個(也可能都用)。第一種思路適合不斷有新頁面出現的網站,比如說小說的新章節,每天的最新新聞等等;第二種思路則適合頁面內容會更新的網站。第三個思路是相當于是最后的一道防線。這樣做可以最大程度上達到去重的目的。
去重的方法
最簡單的去重方式自然是將所有訪問過的URL和其對應的內容保存下來,然后過一段時間重新爬取一次并進行比較,然后決定是否需要覆蓋。這顯然是不實際的,因為會消耗很多資源。目前比較實際的做法就是給URL或者其內容(取決于這個網站采用哪種更新方式)上一個標識,這個標識有個比較好聽的名字,叫數據指紋。
這里很容易想到的一種數據指紋就是哈希值,根據哈希函數的特性,我們可以為任意內容生成一個獨一無二的定長字符串,之后只要比較這個哈希值就行了。哈希值是一個很偉大的發明,幾乎在任何地方都有它的影子,它利用數學特性,計算機只要經過簡單的計算就可以得到唯一的特征值,這個計算過程的開銷基本可以忽略不計,當然這是題外話了。
不過即使用了哈希值,你仍需要一個地方存儲所有的哈希值,并且要能做到方便的取用。如果你的存儲介質是數據庫,一般的數據庫系統都能提供索引,如果把哈希值作為唯一索引呢,這應該是可行的。有些數據庫也提供查詢后再插入的操作,不過本質上應該也是索引。和哈希值類似的還有MD5校驗碼,殊途同歸。
除了自建指紋,其實在發送請求時還有一些技巧,比如說304狀態碼,Last-modified字段,文件大小和MD5簽名。具體參考[8],很好理解,就不細說了。
綜上所述,在數據量不大的時候,幾百個或者就幾千個的時候,簡單自己寫個小函數或者利用集合的特性去重就行了。如果數據量夠大,數據指紋的價值就體現出來了,它可以節省可觀的空間,同時可以引入BloomFilter作為去重的手段。另外,如果要對數據做持久化(簡單說就是去重操作不會被事故影響,比如說斷電),就需要用到Redis數據庫。
Redis
Python的Redis客戶端庫也是開源的,地址是:redis-py。不過在開始之前,你首先需要一個有Redis數據庫運行的主機(搭建一個很簡單)。
關于Redis數據庫還有幾個關鍵詞:key-value,高性能,數據持久化,數據備份,原子操作以及跟這里相關的一個特性:支持集合數據類型。這才是為什么做增量爬取時我們要用到Redis數據庫:我們可以通過將URL或者頁面內容的指紋作為key存入Redis數據庫中的集合里,利用集合的不重復性達到去重的目的,每次爬蟲要處理URL或者頁面時會先去Redis數據庫里檢查一下是否已經存在,因為Redis數據庫著力于key-value形式的存儲,所以這一步的速度將會很可觀;其次Redis可以將內存中的內容持久化到磁盤,并且其每一次操作都是原子操作,這就保證了爬蟲的可靠性,即爬蟲不會應為意外停止而損失數據。
但應該如何將這一特性融入到爬蟲中呢?如果是自己寫的爬蟲代碼,添加上述代碼即可;如果使用的是scrapy框架,我們可以在middleware上下功夫,在spider模塊收到要處理的URL時,寫一個Spider中間件用來判斷這條URL的指紋是否在Redis數據庫中存在,如果存在的話,就直接舍棄這條URL;如果是要判斷頁面的內容是否更新,可以在Download中間件中添加代碼來校驗,原理一樣。當然,數據庫的操作可以用類似write()和query()的方法進行封裝,此處不表。
參考:
(1).https://www.cnblogs.com/zbllly/p/10283943.html
(2).https://blog.csdn.net/yubei2155/article/details/79343893? ?= Python爬蟲定時增量更新數據