1. 程式人生 > >scrapy-redis 和 scrapy ?

scrapy-redis 和 scrapy ?

一、scrapy和scrapy-redis的主要區別在哪裡?
個人認為,scrapy和scrapy-redis不應該討論區別。scrapy 是一個通用的爬蟲框架,其功能比較完善,可以幫你迅速的寫一個簡單爬蟲,並且跑起來。scrapy-redis是為了更方便地實現scrapy分散式爬取,而提供了一些以redis為基礎的元件(注意,scrapy-redis只是一些元件,而不是一個完整的框架)。你可以這麼認為,scrapy是一工廠,能夠出產你要的spider。而scrapy-redis是其他廠商為了幫助scrapy工廠更好的實現某些功能而製造了一些裝置,用於替換scrapy工廠的原裝置。所以要想跑分散式,先讓scrapy工廠搭建起來,再用scrapy-redis裝置去更換scrapy的某些裝置。那麼這些scrapy-redis元件有什麼突出特點呢?他們使用了redis資料庫來替換scrapy原本使用的佇列結構(deque),換了資料結構,那麼相應的操作當然都要換啦,所以與佇列相關的這些元件都做了更換。

二、scrapy-redis提供了哪些元件?
Scheduler、Dupefilter、Pipeline、Spider提供了哪些元件具體見darkrho/scrapy-redis · GitHub

三、為什麼要提供這些元件?
這要從哪哪哪說起(喝口水,默默地望著遠方……)
我們先從scrapy的“待爬佇列”和“Scheduler”入手:咱們玩過爬蟲(什麼玩過,是學習過,研究過,愛過也被虐過)的同學都多多少少有些瞭解,在爬蟲爬取過程當中,有一個主要的資料結構是“待爬佇列”,以及能夠操作這個佇列的排程器(也就是Scheduler啦)。scrapy官方文件對這二者的描述不多,基本上沒提。scrapy使用什麼樣的資料結構來存放待爬取的request呢?其實沒用高大上的資料結構,就是python自帶的collection.deque,不過當然是改造過後的啦(後面所說到的deque均是指scrapy改造之後的佇列,至於如何改造的,就去看程式碼吧)。詳見原始碼

queuelib/queue.py at master · scrapy/queuelib · GitHub
不過咱們用一用deque就會意識到,該怎麼讓兩個以上的Spider共用這個deque呢?答案是,我水平不夠,不知道。那分散式怎麼實現呢,待爬佇列都不能共享,還玩個泥巴呀。scrapy-redis提供了一個解決方法,把deque換成redis資料庫,我們從同一個redis伺服器存放要爬取的request,這樣就能讓多個spider去同一個資料庫裡讀取,這樣分散式的主要問題就解決了嘛。

—————————————————分割線的隨地大小便———————————————————-

那麼問題又來了,我們換了redis來存放佇列,哪scrapy就能直接分散式了麼?(當初天真的我呀~當然不能。我們接著往下說。scrapy中跟“待爬佇列”直接相關的就是排程器“Scheduler”:

scrapy/scheduler.py at master · scrapy/scrapy · GitHub,它負責對新的request進行入列操作(加入deque),取出下一個要爬取的request(從deque中取出)等操作。在scrapy中,Scheduler並不是直接就把deque拿來就粗暴的使用了,而且提供了一個比較高階的組織方法,它把待爬佇列按照優先順序建立了一個字典結構,比如:
{
priority0:佇列0
priority1:佇列2
priority2:佇列2
}
然後根據request中的priority屬性,來決定該入哪個佇列。而出列時,則按priority較小的優先出列。為了管理這個比較高階的佇列字典,Scheduler需要提供一系列的方法。說這麼多有什麼意義呢?最主要的指導意義就是:你要是換了redis做佇列,這個scrapy下的Scheduler就用不了,所以自己寫一個吧。於是就出現了scrapy-redis的專用scheduler:scrapy-redis/scheduler.py at master · darkrho/scrapy-redis · GitHub,其實可以對比一下看,操作什麼的都差不太多。主要是操作的資料結構變了。

———————————————-被當眾抓住的分割線———————————————————

那麼既然使用了redis做主要資料結構,能不能把其他使用自帶資料結構關鍵功能模組也換掉呢?(關鍵部分使用自帶資料結構簡直有種沒穿小內內的危機感,這是本人的想法~)在我們爬取過程當中,還有一個重要的功能模組,就是request去重(已經發送過得請求就別再發啦,也要考慮一下伺服器君的感受好伐)。scrapy中是如何實現這個去重功能的呢?用集合~scrapy中把已經發送的request指紋放入到一個集合中,把下一個request的指紋拿到集合中比對,如果該指紋存在於集合中,說明這個request傳送過了,如果沒有則繼續操作。這個核心的判重功能是這樣實現的:

def request_seen(self, request):
    #self.figerprints就是一個指紋集合
    fp = self.request_fingerprint(request)
    if fp in self.fingerprints:#這就是判重的核心操作。
        return True
    self.fingerprints.add(fp)
    ......

——————————————把分割線掀起來(╯‵□′)╯︵||||||\\\\\\————————————

那麼依次類推,接下來的其他元件(Pipeline和Spider),我們也可以輕鬆的猜到,他們是為什麼要被修改呢。對,都是因為redis,全怪redis,redis是罪魁禍首。