1. 程式人生 > >python之scrapy(五)分散式爬蟲

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'

#最大空閒時間防止分散式爬蟲因為等待而關閉

#這隻有當上面設定的佇列類是SpiderQueueSpiderStack時才有效

#並且當您的蜘蛛首次啟動時,也可能會阻止同一時間啟動(由於佇列為空)

#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

#指定用於連線redisURL(可選)

#如果設定此項,則此項優先順序高於設定的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

#RedisSpiderRedisCrawlSpider預設 start_usls

#REDIS_START_URLS_KEY = '%(name)s:start_urls'

#設定redis使用utf-8之外的編碼

#REDIS_ENCODING = 'latin1'

請各位小夥伴兒自行挑選需要的配置寫到專案的settings.py檔案中。