關於爬蟲 retry 機制的思考

Cold_Chair發表於2024-11-28

背景:

最近在爬某網站,發現其反爬機制是這樣的:

如果一段時間訪問次數比較多,就會禁止訪問幾分鐘,然後恢復正常。

對於爬蟲端,這其實很難針對寫:

發生禁止訪問時,爬蟲並不知道這是突發性錯誤(臨時網路掛了,過幾秒可能就好了),還是資源本身不能訪問(永遠會 fail),還是網站反爬機制起作用了。

如果能檢測反爬蟲機制是否生效,倒是很好解決這個問題,但是這不好寫(不同網站機制不一樣)。

假設我們不能檢測反爬蟲機制,有沒有什麼通用的演算法策略呢?

如果我們只採用 retry 的方法:
比如說 retry 3 次,但是反爬機制生效的這段時間,怎麼都是訪問不了的,所以會錯過資源。

如果我們採用 sleep + retry 的方法,有 2 個問題:

  1. 不知道要 sleep 多長時間
  2. 如果對每個訪問不了資源都 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。

相關文章