背景:
最近在爬某網站,發現其反爬機制是這樣的:
如果一段時間訪問次數比較多,就會禁止訪問幾分鐘,然後恢復正常。
對於爬蟲端,這其實很難針對寫:
發生禁止訪問時,爬蟲並不知道這是突發性錯誤(臨時網路掛了,過幾秒可能就好了),還是資源本身不能訪問(永遠會 fail),還是網站反爬機制起作用了。
如果能檢測反爬蟲機制是否生效,倒是很好解決這個問題,但是這不好寫(不同網站機制不一樣)。
假設我們不能檢測反爬蟲機制,有沒有什麼通用的演算法策略呢?
如果我們只採用 retry 的方法:
比如說 retry 3 次,但是反爬機制生效的這段時間,怎麼都是訪問不了的,所以會錯過資源。
如果我們採用 sleep + retry 的方法,有 2 個問題:
- 不知道要 sleep 多長時間
- 如果對每個訪問不了資源都 sleep 再 retry,那麼每個本身不能訪問的資源都會浪費 sleep 時間 * retry 次數
策略:
下面介紹一個我想到的演算法策略,可以適配於併發的爬蟲程式,效率也比較高:
我們維護一個佇列 Q:佇列存訪問失敗的資源,和嘗試過的次數。
假設我們有一個爬蟲的協程池(併發池),
當某個爬蟲訪問一個資源失敗時,加入佇列 Q。
當某個爬蟲訪問一個資源成功時,掛起協程池(訪問完當前資源就清空)。然後 retry 一輪佇列 Q。
一輪 retry :
某個資源成功了,pop。
如果佇列 Q 裡的 head 資源訪問次數達到上限,pop。
爬蟲任務結束時,如果 Q 不為空,一輪一輪 retry Q,直到 Q 為空。
retry 每一輪之間設定一個間隔(比如說 2 分鐘,可以觀察之前 fail 到 success 的間隔來設定)
策略需求:
沒有 retry 機制時,連續 fail 長度 + 協程池大小 < 連續 success 長度
不然一輪 retry 到中間時,又被反爬了,就會導致佇列 Q 的 tail 部分直接嘗試次數 + 1。
程式碼參考:
Codeforces 的 blog 爬蟲:
等我上傳 github。