python之scrapy(五)分散式爬蟲
Scrapy是一個比較好用的Python爬蟲框架,你只需要編寫幾個元件就可以實現網頁資料的爬取。但是當我們要爬取的頁面非常多的時候,單個主機的處理能力就不能滿足我們的需求了(無論是處理速度還是網路請求的併發數),這時候分散式爬蟲的優勢就顯現出來。
一、分散式爬蟲的原理
下面是單機版本的Scrapy框架:
Scrapy單機爬蟲中只有一個本地爬取佇列Queue,如果新的Request生成,就放到佇列裡面,隨後Request被Scheduler排程。之後Request交給DownLoader執行爬取,簡單的排程框架如下圖所示:
如果兩個Scheduler同時從訊息隊裡裡面取Request,每個Scheduler都有對應的DownLoader,那麼在頻寬足夠、正常爬取切不考慮佇列壓力的情況下,爬蟲效率就會翻倍。
這樣,Sheduler可以擴充套件為多個,DownLoader也是多個,而爬取佇列維持為一個,也就是所謂的共享爬蟲佇列。這樣才能保證Scheduler從佇列裡面排程某個Request之後,其他的Scheduler不會重複呼叫該Request,就可以保證多個Scheduler同步爬取。
Scheduler進行排程,而要實現多臺伺服器共同爬取資料關鍵就是共享爬取佇列。
我們需要做的是在多臺主機上同時執行爬蟲任務協同爬取,而協同爬取的的前提就是共享爬蟲佇列。這樣各臺主機不需要各自維護排重佇列,各臺主機還是有各自的Sheduler和Downloader,所以排程和下載功能在各自的主機上完成。
二、維護爬蟲佇列
這裡一般我們通過Redis為維護,Redis,非關係型資料庫,Key-Value形式儲存,結構靈活;並且redis是記憶體中的資料結構儲存系統,處理速度快;提供佇列集合等多種儲存結構,方便佇列維護。
-
列表有lpush()、rpush()、lpop()、rpop()方法,可以實現先進先出式爬取佇列,也可以實現先進後出堆疊式爬取佇列;
-
集合的元素是無序,並且不重複的,這樣就可以非常方便的實現隨機排序且不重複的爬取佇列;
-
有序集合帶有分數表示,而Scrapy的Request也有優先順序的控制,我們可以用它來實現優先順序排程佇列。
三、如何去重
這裡藉助redis的集合,redis提供集合資料結構,在redis集合中儲存每個request的指紋,在向request佇列中加入Request前先驗證這個Request的指紋是否已經加入集合中。如果已經存在則不新增到request佇列中,如果不存在,則將request加入到佇列並將指紋加入集合。
四、如何防止中斷
這裡是做了啟動判斷,在每臺slave的Scrapy啟動的時候都會判斷當前redis request佇列是否為空。如果不為空,則從佇列中獲取下一個request執行爬取。如果為空則重新開始爬取,第一臺從機執行爬取向佇列中新增request。
五、架構的實現
接下來主要在需要在程式裡面實現這個框架。首先實現一個共享佇列,還要實現去重的功能。另外還有一個Sheduler的實現,確保可以從訊息佇列裡面存取Request。
目前已經有人實現的框架scrapy-redis:
六、Scrapy-Redis
Scrapy-Redis則是一個基於Redis的Scrapy分散式元件。它利用Redis對用於爬取的請求(Requests)進行儲存和排程(Schedule),並對爬取產生的專案(items)儲存以供後續處理使用。scrapy-redi重寫了scrapy一些比較關鍵的程式碼,將scrapy變成一個可以在多個主機上同時執行的分散式爬蟲。
參考資料:
七、如何配置
#啟用Redis排程儲存請求佇列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#確保所有的爬蟲通過Redis去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#預設請求序列化使用的是pickle 但是我們可以更改為其他類似的。PS:這玩意兒2.X的可以用。3.X的不能用
#SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
#不清除Redis佇列
#SCHEDULER_PERSIST = True
#使用優先順序排程請求佇列 (預設使用)
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
#可選用的其它佇列
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
#最大空閒時間防止分散式爬蟲因為等待而關閉
#這隻有當上面設定的佇列類是SpiderQueue或SpiderStack時才有效
#並且當您的蜘蛛首次啟動時,也可能會阻止同一時間啟動(由於佇列為空)
#SCHEDULER_IDLE_BEFORE_CLOSE = 10
#將清除的專案在redis進行處理
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
}
#序列化專案管道作為redis Key儲存
#REDIS_ITEMS_KEY = '%(spider)s:items'
#預設使用ScrapyJSONEncoder進行專案序列化
#You can use any importable path to a callable object.
#REDIS_ITEMS_SERIALIZER = 'json.dumps'
#指定連線到redis時使用的埠和地址(可選)
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379
#指定用於連線redis的URL(可選)
#如果設定此項,則此項優先順序高於設定的REDIS_HOST 和 REDIS_PORT
REDIS_URL = 'redis://user:[email protected]:9001'
#自定義的redis引數(連線超時之類的)
#REDIS_PARAMS = {}
#自定義redis客戶端類
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
#如果為True,則使用redis的'spop'進行操作。
#如果需要避免起始網址列表出現重複,這個選項非常有用。開啟此選項urls必須通過sadd新增,否則會出現型別錯誤。
#REDIS_START_URLS_AS_SET = False
#RedisSpider和RedisCrawlSpider預設 start_usls 鍵
#REDIS_START_URLS_KEY = '%(name)s:start_urls'
#設定redis使用utf-8之外的編碼
#REDIS_ENCODING = 'latin1'
請各位小夥伴兒自行挑選需要的配置寫到專案的settings.py檔案中。