Scrapy 和 scrapy-redis的區別

Scrapy 是一個通用的爬蟲框架,但是不支持分佈式,Scrapy-redis是為了更方便地實現Scrapy分佈式爬取,而提供了一些以redis為基礎的組件(僅有組件)。

pip install scrapy-redis

Scrapy-redis提供了下面四種組件(components):(四種組件意味著這四個模塊都要做相應的修改)

  • Scheduler
  • Duplication Filter
  • Item Pipeline
  • Base Spider

scrapy-redis架構

Scrapy 和 scrapy-redis的區別

如上圖所⽰示,scrapy-redis在scrapy的架構上增加了redis,基於redis的特性拓展瞭如下組件:

Scheduler:

Scrapy改造了python本來的collection.deque(雙向隊列)形成了自己的Scrapy queue(https://github.com/scrapy/queuelib/blob/master/queuelib/queue.py)),但是Scrapy多個spider不能共享待爬取隊列Scrapy queue, 即Scrapy本身不支持爬蟲分佈式,scrapy-redis 的解決是把這個Scrapy queue換成redis數據庫(也是指redis隊列),從同一個redis-server存放要爬取的request,便能讓多個spider去同一個數據庫裡讀取。

Scrapy中跟“待爬隊列”直接相關的就是調度器Scheduler,它負責對新的request進行入列操作(加入Scrapy queue),取出下一個要爬取的request(從Scrapy queue中取出)等操作。它把待爬隊列按照優先級建立了一個字典結構,比如:

 {
優先級0 : 隊列0
優先級1 : 隊列1
優先級2 : 隊列2
}

然後根據request中的優先級,來決定該入哪個隊列,出列時則按優先級較小的優先出列。為了管理這個比較高級的隊列字典,Scheduler需要提供一系列的方法。但是原來的Scheduler已經無法使用,所以使用Scrapy-redis的scheduler組件。

Duplication Filter

Scrapy中用集合實現這個request去重功能,Scrapy中把已經發送的request指紋放入到一個集合中,把下一個request的指紋拿到集合中比對,如果該指紋存在於集合中,說明這個request發送過了,如果沒有則繼續操作。這個核心的判重功能是這樣實現的:

 def request_seen(self, request):
# 把請求轉化為指紋
fp = self.request_fingerprint(request)

# 這就是判重的核心操作 ,self.fingerprints就是指紋集合
if fp in self.fingerprints:
return True #直接返回
self.fingerprints.add(fp) #如果不在,就添加進去指紋集合
if self.file:
self.file.write(fp + os.linesep)

在scrapy-redis中去重是由Duplication Filter組件來實現的,它通過redis的set 不重複的特性,巧妙的實現了Duplication Filter去重。scrapy-redis調度器從引擎接受request,將request的指紋存⼊redis的set檢查是否重複,並將不重複的request push寫⼊redis的 request queue。

引擎請求request(Spider發出的)時,調度器從redis的request queue隊列⾥里根據優先級pop 出⼀個request 返回給引擎,引擎將此request發給spider處理。

Item Pipeline:

引擎將(Spider返回的)爬取到的Item給Item Pipeline,scrapy-redis 的Item Pipeline將爬取到的 Item 存⼊redis的 items queue。

修改過Item Pipeline可以很方便的根據 key 從 items queue 提取item,從⽽實現 items processes集群。

Base Spider

不在使用scrapy原有的Spider類,重寫的RedisSpider繼承了Spider和RedisMixin這兩個類,RedisMixin是用來從redis讀取url的類。

當我們生成一個Spider繼承RedisSpider時,調用setup_redis函數,這個函數會去連接redis數據庫,然後會設置signals(信號):

  • 一個是當spider空閒時候的signal,會調用spider_idle函數,這個函數調用schedule_next_request函數,保證spider是一直活著的狀態,並且拋出DontCloseSpider異常。
  • 一個是當抓到一個item時的signal,會調用item_scraped函數,這個函數會調用schedule_next_request函數,獲取下一個request。


分享到:


相關文章: