最近在寫一個基於代理池的高併發爬蟲,目標是用單機從某網站 API 爬取十億級別的JSON數據。 代理池 有兩種方式能夠實現爬蟲對代理池的充分利用: 搭建一個 Tunnel Proxy 伺服器維護代理池 在爬蟲項目內部自動切換代理 所謂 Tunnel Proxy 實際上是將切換代理的操作交給了代理服務 ...
最近在寫一個基於代理池的高併發爬蟲,目標是用單機從某網站 API 爬取十億級別的JSON數據。
代理池
有兩種方式能夠實現爬蟲對代理池的充分利用:
- 搭建一個 Tunnel Proxy 伺服器維護代理池
- 在爬蟲項目內部自動切換代理
所謂 Tunnel Proxy 實際上是將切換代理的操作交給了代理伺服器,很多市面上的代理軟體都有此類功能。
如果要自行搭建可參考以下項目:
考慮到高併發,在爬蟲項目內部切換代理更加靈活一些。代理池選一個能用的就行:GitHub - jhao104/proxy_pool
記得加上匿名校驗:能否設置代理池只獲取高匿IP · Issue #169 · jhao104/proxy_pool · GitHub
代理切換策略
如果簡單的在多線程中對每個 requests.get() 使用不同的代理,那麼一定會遇到記憶體泄露的問題:
- 記憶體泄露問題 · Issue #522 · jhao104/proxy_pool · GitHub
- Requests memory leak · Issue #4601 · psf/requests · GitHub
- Memory Leak in Python requests - GeeksforGeeks
即便寫成:
session = requests.session()
response = session.get(url, headers=headers, proxies=proxies)
response.close()
session.close()
甚至在加上 gc.collect() 也無濟於事。
因此需要控制創建 session 對象的數量,只在請求失敗後切換代理和創建新的 session。
工作流程
① 主線程根據 URL 數量動態創建子進程,虛線框內為子進程任務
② crawler_task 為線程任務,執行發送請求和解析JSON
插入策略
每個子進程維護一個 url_queue 和 insert_queue。
線程會從 url_queue 取出URL執行爬取任務,由於JSON數據占用的空間不大,所以線程會先將每個 response 經過簡單解析後存到列表中。
等到 url_queue 為空時(不要使用不安全的 queue.empty() 判斷),get 方法會觸發 Timeout 異常,然後線程會將列表插入到 insert_queue 中。
所有線程任務結束後,子進程再執行 executemany 將數據批量插入到 MySQL。
其他
爬取JSON數據產生的流量不大,但需要考慮 PPS(packet per second),如果網路設施不到位的話可能嚴重影響爬取效率。
網路上獲取的免費代理大多是透明代理,如果使用開源項目 Proxy_Pool 作為代理池並加入匿名校驗,可能會間歇性導致代理池沒有可用代理。(所以最好還是從一些網路空間測繪引擎上通過特征抓取)