Python3網路爬蟲開發實戰 分布式爬蟲原理

2021-10-01 08:20:27 字數 4203 閱讀 3544

分布式爬蟲原理

我們在前面已經實現了 scrapy 微博爬蟲,雖然爬蟲是非同步加多執行緒的,但是我們只能在一台主機上執行,所以爬取效率還是有限的,分布式爬蟲則是將多台主機組合起來,共同完成乙個爬取任務,這將大大提高爬取的效率。

分布式爬蟲架構

在了解分布式爬蟲架構之前,首先回顧一下 scrapy 的架構,如圖 13-1 所示。

scrapy 單機爬蟲中有乙個本地爬取佇列 queue,這個佇列是利用 deque 模組實現的。如果新的 request 生成就會放到佇列裡面,隨後 request 被 scheduler 排程。之後,request 交給 ********** 執行爬取,簡單的排程架構如圖 14-1 所示。

圖 14-1 排程架構

如果兩個 scheduler 同時從佇列裡面取 request,每個 scheduler 都有其對應的 **********,那麼在頻寬足夠、正常爬取且不考慮佇列訪問壓力的情況下,爬取效率會有什麼變化?沒錯,爬取效率會翻倍。

這樣,scheduler 可以擴充套件多個,********** 也可以擴充套件多個。而爬取佇列 queue 必須始終為乙個,也就是所謂的共享爬取佇列。這樣才能保證 scheduer 從佇列裡排程某個 request 之後,其他 scheduler 不會重複排程此 request,就可以做到多個 schduler 同步爬取。這就是分布式爬蟲的基本雛形,簡單排程架構如圖 14-2 所示。

圖 14-2 排程架構

維護爬取佇列

那麼這個佇列用什麼維護來好呢?我們首先需要考慮的就是效能問題,什麼資料庫訪問效率高?我們自然想到基於記憶體儲存的 redis,而且 redis 還支援多種資料結構,例如列表 list、集合 set、有序集合 sorted set 等等,訪問的操作也非常簡單,所以在這裡我們採用 redis 來維護爬取佇列。

這幾種資料結構儲存實際各有千秋,分析如下:

列表資料結構有 lpush()、lpop()、rpush()、rpop() 方法,所以我們可以用它來實現乙個先進先出式爬取佇列,也可以實現乙個先進後出棧式爬取佇列。

集合的元素是無序的且不重複的,這樣我們可以非常方便地實現乙個隨機排序的不重複的爬取佇列。

有序集合帶有分數表示,而 scrapy 的 request 也有優先順序的控制,所以用有集合我們可以實現乙個帶優先順序排程的佇列。

這些不同的佇列我們需要根據具體爬蟲的需求靈活選擇。

怎樣來去重

scrapy 有自動去重,它的去重使用了 python 中的集合。這個集合記錄了 scrapy 中每個 request 的指紋,這個指紋實際上就是 request 的雜湊值。我們可以看看 scrapy 的源**,如下所示:

import hashlib

def request_fingerprint(request, include_headers=none):

if include_headers:

include_headers = tuple(to_bytes(h.lower(

))for h in sorted(include_headers))

cache = _fingerprint_cache.setdefault(request,

)if include_headers not in cache:

fp = hashlib.sha1(

) fp.update(to_bytes(request.method))

fp.update(to_bytes(canonicalize_url(request.url))

) fp.update(request.body or b''

)if include_headers:

for hdr in include_headers:

if hdr in request.headers:

fp.update(hdr)

forv

in request.headers.getlist(hdr):

fp.update(v)

cache[include_headers]

= fp.hexdigest(

)return cache[include_headers]

request_fingerprint() 就是計算 request 指紋的方法,其方法內部使用的是 hashlib 的 sha1() 方法。計算的字段包括 request 的 method、url、body、headers 這幾部分內容,這裡只要有一點不同,那麼計算的結果就不同。計算得到的結果是加密後的字串,也就是指紋。每個 request 都有獨有的指紋,指紋就是乙個字串,判定字串是否重複比判定 request 物件是否重複容易得多,所以指紋可以作為判定 request 是否重複的依據。

那麼我們如何判定重複呢?scrapy 是這樣實現的,如下所示:

def __init__(self):

self.fingerprints = set()

def request_seen(self, request):

fp = self.request_fingerprint(request)

if fp in self.fingerprints:

return true

self.fingerprints.add(fp)

在去重的類 rfpdupefilter 中,有乙個 request_seen() 方法,這個方法有乙個引數 request,它的作用就是檢測該 request 物件是否重複。這個方法呼叫 request_fingerprint() 獲取該 request 的指紋,檢測這個指紋是否存在於 fingerprints 變數中,而 fingerprints 是乙個集合,集合的元素都是不重複的。如果指紋存在,那麼就返回 true,說明該 request 是重複的,否則這個指紋加入到集合中。如果下次還有相同的 request 傳遞過來,指紋也是相同的,那麼這時指紋就已經存在於集合中,request 物件就會直接判定為重複。這樣去重的目的就實現了。

scrapy 的去重過程就是,利用集合元素的不重複特性來實現 request 的去重。

對於分布式爬蟲來說,我們肯定不能再用每個爬蟲各自的集合來去重了。因為這樣還是每個主機單獨維護自己的集合,不能做到共享。多台主機如果生成了相同的 request,只能各自去重,各個主機之間就無法做到去重了。

那麼要實現去重,這個指紋集合也需要是共享的,redis 正好有集合的儲存資料結構,我們可以利用 redis 的集合作為指紋集合,那麼這樣去重集合也是利用 redis 共享的。每台主機新生成 request 之後,把該 request 的指紋與集合比對,如果指紋已經存在,說明該 request 是重複的,否則將 request 的指紋加入到這個集合中即可。利用同樣的原理不同的儲存結構我們也實現了分布式 reqeust 的去重。

防止中斷

在 scrapy 中,爬蟲執行時的 request 佇列放在記憶體中。爬蟲執行中斷後,這個佇列的空間就被釋放,此佇列就被銷毀了。所以一旦爬蟲執行中斷,爬蟲再次執行就相當於全新的爬取過程。

要做到中斷後繼續爬取,我們可以將佇列中的 request 儲存起來,下次爬取直接讀取儲存資料即可獲取上次爬取的佇列。我們在 scrapy 中指定乙個爬取佇列的儲存路徑即可,這個路徑使用 job_dir 變數來標識,我們可以用如下命令來實現:

scrapy crawl spider -s jobdir=crawls/spider
在 scrapy 中,我們實際是把爬取佇列儲存到本地,第二次爬取直接讀取並恢復佇列即可。那麼在分布式架構中我們還用擔心這個問題嗎?不需要。因為爬取佇列本身就是用資料庫儲存的,如果爬蟲中斷了,資料庫中的 request 依然是存在的,下次啟動就會接著上次中斷的地方繼續爬取。

所以,當 redis 的隊列為空時,爬蟲會重新爬取;當 redis 的佇列不為空時,爬蟲便會接著上次中斷之處繼續爬取。

架構實現

我們接下來就需要在程式中實現這個架構了。首先實現乙個共享的爬取佇列,還要實現去重的功能。另外,重寫乙個 scheduer 的實現,使之可以從共享的爬取佇列訪問 request。

幸運的是,已經有人實現了這些邏輯和架構,並發布成叫 scrapy-redis 的 python 包。接下來,我們看看 scrapy-redis 的原始碼實現,以及它的詳細工作原理。

《Python3網路爬蟲開發實戰》爬蟲有關庫的安裝

pip install requests pip install selenium 將chromedriver.exe檔案放到python的scripts目錄下 將其路徑配置到環境變數 將geckodriver.exe檔案放到python的scripts目錄下 將其路徑配置到環境變數 解壓後將bin...

python3 網路爬蟲開發實戰 貓眼top100

我發現自己沒有整理和總結的習慣,有時是學了之後覺得會了,懶得整理,有時是沒有時間,偶爾有時候想起來會寫一篇。但是後來發現忘的還是挺快的,而且想找以前的東西的時候總是不太方便。不過人生在世,總要給這個世界留下點什麼。把自己在學習中得到的東西,所思所想都記錄下來,所以在此立個flag 狗頭 養成總結和寫...

Python 3 網路爬蟲

python 原來還可以這樣玩 python爬蟲,破解有道翻譯介面引數 破解有道翻譯反爬蟲機制 python3網路爬蟲快速入門實戰解析 article details 78123502 python3網路爬蟲 五 python3安裝scrapy article details 60156205 py...