scrapy-redis(0.6)依賴的環(huán)境
Scrapy >= 1.0.0 #終于過(guò)了1版本劫窒,這個(gè)太重要了,總算坑小了點(diǎn)燃乍,感謝那些為了解決各種scrapy與scrapy-redis不兼容做出了貢獻(xiàn)的開(kāi)發(fā)者和博主悟泵。redis-py >= 2.10.0redis server >= 2.8.0
0.6版本的主要更新內(nèi)容是更新代碼以支持Scrapy 1.0香到; 增加了-a domain=... option for example spiders.
雖然scrapy能做的事情很多图柏,但是要做到大規(guī)模的分布式應(yīng)用則捉襟見(jiàn)肘序六。有能人改變了scrapy的隊(duì)列調(diào)度,將起始的網(wǎng)址從start_urls里分離出來(lái)蚤吹,改為從redis讀取例诀,多個(gè)客戶端可以同時(shí)讀取同一個(gè)redis,從而實(shí)現(xiàn)了分布式的爬蟲(chóng)裁着。
scrapy-redis架構(gòu):
scrapy-Redis組件詳解:
四種組件:
Scheduler繁涂,Duplication Filter,Item Pipeline二驰,Base Spider
1扔罪、Scheduler: scrapy改造了python本來(lái)的collection.deque(雙向隊(duì)列)形成了自己的Scrapy
queue,但是Scrapy多個(gè)spider不能共享待爬取隊(duì)列Scrapy
queue桶雀,即Scrapy本身不支持爬蟲(chóng)分布式矿酵,scrapy-redis 的解決是把這個(gè)Scrapy
queue換成redis數(shù)據(jù)庫(kù)(也是指redis隊(duì)列),從同一個(gè)redis-server存放要爬取的request矗积,便能讓多個(gè)spider去同一個(gè)數(shù)據(jù)庫(kù)里讀取全肮。Scrapy中跟“待爬隊(duì)列”直接相關(guān)的就是調(diào)度器Scheduler,它負(fù)責(zé)對(duì)新的request進(jìn)行入列操作(加入Scrapy
queue)漠魏,取出下一個(gè)要爬取的request(從Scrapy
queue中取出)等操作倔矾。它把待爬隊(duì)列按照優(yōu)先級(jí)建立了一個(gè)字典結(jié)構(gòu),然后根據(jù)request中的優(yōu)先級(jí)柱锹,來(lái)決定該入哪個(gè)隊(duì)列哪自,出列時(shí)則按優(yōu)先級(jí)較小的優(yōu)先出列。為了管理這個(gè)比較高級(jí)的隊(duì)列字典禁熏,Scheduler需要提供一系列的方法壤巷。但是原來(lái)的Scheduler已經(jīng)無(wú)法使用,所以使用Scrapy-redis的scheduler組件瞧毙。
2胧华、Duplication Filter
Scrapy中用集合實(shí)現(xiàn)這個(gè)request去重功能,Scrapy中把已經(jīng)發(fā)送的request指紋放入到一個(gè)集合中宙彪,把下一個(gè)request的指紋拿到集合中比對(duì)矩动,如果該指紋存在于集合中,說(shuō)明這個(gè)request發(fā)送過(guò)了释漆,如果沒(méi)有則繼續(xù)操作悲没。這個(gè)核心的判重功能是這樣實(shí)現(xiàn)的:
在scrapy-redis中去重是由Duplication Filter組件來(lái)實(shí)現(xiàn)的,它通過(guò)redis的set不重復(fù)的特性男图,巧妙的實(shí)現(xiàn)了DuplicationFilter去重示姿。scrapy-redis調(diào)度器從引擎接受request甜橱,將request的指紋存入redis的set檢查是否重復(fù),并將不重復(fù)的request push寫(xiě)入redis的 request queue栈戳。
引擎請(qǐng)求request(Spider發(fā)出的)時(shí)岂傲,調(diào)度器從redis的request queue隊(duì)列里根據(jù)優(yōu)先級(jí)pop 出?個(gè)request 返回給引擎,引擎將此request發(fā)給spider處理子檀。
3镊掖、Item Pipeline:
引擎將(Spider返回的)爬取到的Item給Item Pipeline,scrapy-redis 的Item Pipeline將爬取到的
Item 存入redis的 items queue命锄。修改過(guò)Item Pipeline可以很方便的根據(jù) key 從 items queue
提取item堰乔,從而實(shí)現(xiàn) items processes集群。
4脐恩、Base Spider
不在使用scrapy原有的Spider類镐侯,重寫(xiě)的RedisSpider繼承了Spider和RedisMixin這兩個(gè)類,RedisMixin是用來(lái)從redis讀取url的類驶冒。
當(dāng)我們生成一個(gè)Spider繼承RedisSpider時(shí)苟翻,調(diào)用setup_redis函數(shù),這個(gè)函數(shù)會(huì)去連接redis數(shù)據(jù)庫(kù)骗污,然后會(huì)設(shè)置signals(信號(hào)):一個(gè)是當(dāng)spider空閑時(shí)候的signal崇猫,會(huì)調(diào)用spider_idle函數(shù),這個(gè)函數(shù)調(diào)用schedule_next_request函數(shù)需忿,保證spider是一直活著的狀態(tài)诅炉,并且拋出DontCloseSpider異常。一個(gè)是當(dāng)抓到一個(gè)item時(shí)的signal屋厘,會(huì)調(diào)用item_scraped函數(shù)涕烧,這個(gè)函數(shù)會(huì)調(diào)用schedule_next_request函數(shù),獲取下一個(gè)request汗洒。
5议纯、 總結(jié)
總結(jié)一下scrapy-redis的總體思路:這套組件通過(guò)重寫(xiě)scheduler和spider類,實(shí)現(xiàn)了調(diào)度溢谤、spider啟動(dòng)和redis的交互瞻凤;
實(shí)現(xiàn)新的dupefilter和queue類,達(dá)到了判重和調(diào)度容器和redis的交互世杀,因?yàn)槊總€(gè)主機(jī)上的爬蟲(chóng)進(jìn)程都訪問(wèn)同一個(gè)redis數(shù)據(jù)庫(kù)阀参,所以調(diào)度和判重都統(tǒng)一進(jìn)行統(tǒng)一管理,達(dá)到了分布式爬蟲(chóng)的目的瞻坝;當(dāng)spider被初始化時(shí)蛛壳,同時(shí)會(huì)初始化一個(gè)對(duì)應(yīng)的scheduler對(duì)象,這個(gè)調(diào)度器對(duì)象通過(guò)讀取settings,配置好自己的調(diào)度容器queue和判重工具dupefilter炕吸;
每當(dāng)一個(gè)spider產(chǎn)出一個(gè)request的時(shí)候,scrapy引擎會(huì)把這個(gè)reuqest遞交給這個(gè)spider對(duì)應(yīng)的scheduler對(duì)象進(jìn)行調(diào)度勉痴,scheduler對(duì)象通過(guò)訪問(wèn)redis對(duì)request進(jìn)行判重赫模,如果不重復(fù)就把他添加進(jìn)redis中的調(diào)度器隊(duì)列里。當(dāng)調(diào)度條件滿足時(shí)蒸矛,scheduler對(duì)象就從redis的調(diào)度器隊(duì)列中取出一個(gè)request發(fā)送給spider瀑罗,讓他爬取雏掠;
當(dāng)spider爬取的所有暫時(shí)可用url之后斩祭,scheduler發(fā)現(xiàn)這個(gè)spider對(duì)應(yīng)的redis的調(diào)度器隊(duì)列空了,于是觸發(fā)信號(hào)spider_idle乡话,spider收到這個(gè)信號(hào)之后摧玫,直接連接redis讀取strart_url池,拿去新的一批url入口绑青,然后再次重復(fù)上邊的工作诬像。
scrapy-Redis分布式策略:
Slaver端從Master端拿任務(wù)(Request/url/ID)進(jìn)行數(shù)據(jù)抓取,在抓取數(shù)據(jù)的同時(shí)也生成新任務(wù)闸婴,并將任務(wù)拋給Master坏挠。Master端只有一個(gè)Redis數(shù)據(jù)庫(kù),負(fù)責(zé)對(duì)Slaver提交的任務(wù)進(jìn)行去重邪乍、加入待爬隊(duì)列降狠。
優(yōu)點(diǎn):scrapy-redis默認(rèn)使用的就是這種策略,我們實(shí)現(xiàn)起來(lái)很簡(jiǎn)單庇楞,因?yàn)槿蝿?wù)調(diào)度等工作scrapy-redis都已經(jīng)幫我們做好了榜配,我們只需要繼承RedisSpider、指定redis_key就行了姐刁。
缺點(diǎn):scrapy-redis調(diào)度的任務(wù)是Request對(duì)象芥牌,里面信息量比較大(不僅包含url,還有callback函數(shù)聂使、headers等信息)壁拉,導(dǎo)致的結(jié)果就是會(huì)降低爬蟲(chóng)速度、而且會(huì)占用Redis大量的存儲(chǔ)空間柏靶。當(dāng)然我們可以重寫(xiě)方法實(shí)現(xiàn)調(diào)度url弃理。
創(chuàng)建項(xiàng)目
這個(gè)爬蟲(chóng)繼承了RedisSpider,它能夠支持分布式的抓取屎蜓,采用的是basic spider痘昌,需要寫(xiě)parse函數(shù)。其次就是不再有start_urls了,取而代之的是redis_key辆苔,scrapy-redis將key從Redis里pop出來(lái)算灸,成為請(qǐng)求的url地址。
修改settings.py
修改設(shè)置settings.py
①Scheduler驻啤,首先是Scheduler的替換菲驴,這個(gè)東西是Scrapy中的調(diào)度員。在settings.py中添加以下代碼:
SCHEDULER="scrapy_redis.scheduler.Scheduler"
②去重
DUPEFILTER_CLASS="scrapy_redis.dupefilter.RFPDupeFilter"
③不清理Redis隊(duì)列
SCHEDULER_PERSIST=True
如果這一項(xiàng)為True骑冗,那么在Redis中的URL不會(huì)被Scrapy_redis清理掉赊瞬,這樣的好處是:爬蟲(chóng)停止了再重新啟動(dòng),它會(huì)從上次暫停的地方開(kāi)始繼續(xù)爬取贼涩。但是它的弊端也很明顯巧涧,如果有多個(gè)爬蟲(chóng)都要從這里讀取URL,需要另外寫(xiě)一段代碼來(lái)防止重復(fù)爬取遥倦。
如果設(shè)置成了False谤绳,那么Scrapy_redis每一次讀取了URL以后,就會(huì)把這個(gè)URL給刪除袒哥。這樣的好處是:多個(gè)服務(wù)器的爬蟲(chóng)不會(huì)拿到同一個(gè)URL闷供,也就不會(huì)重復(fù)爬取。但弊端是:爬蟲(chóng)暫停以后再重新啟動(dòng)统诺,它會(huì)重新開(kāi)始爬歪脏。
④設(shè)置redis地址
啟用本地redis:? REDIS_URL = 'redis://127.0.0.1:6379'
啟用遠(yuǎn)程redis:? REDIS_URL = 'redis://39.106.155.194:6379'
其他設(shè)置(可選)
爬蟲(chóng)請(qǐng)求的調(diào)度算法
爬蟲(chóng)的請(qǐng)求調(diào)度算法,有三種情況可供選擇:
①隊(duì)列
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderQueue'
如果不配置調(diào)度算法粮呢,默認(rèn)就會(huì)使用這種方式婿失。它實(shí)現(xiàn)了一個(gè)先入先出的隊(duì)列,先放進(jìn)Redis的請(qǐng)求會(huì)優(yōu)先爬取啄寡。
②棧
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderStack'
這種方式豪硅,后放入到Redis的請(qǐng)求會(huì)優(yōu)先爬取。
③優(yōu)先級(jí)隊(duì)列
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderPriorityQueue'
這種方式挺物,會(huì)根據(jù)一個(gè)優(yōu)先級(jí)算法來(lái)計(jì)算哪些請(qǐng)求先爬取懒浮,哪些請(qǐng)求后爬取。這個(gè)優(yōu)先級(jí)算法比較復(fù)雜识藤,會(huì)綜合考慮請(qǐng)求的深度等各個(gè)因素砚著。
呼~(yú)~配置完這些,爬蟲(chóng)就可以正常工作了痴昧,slave從master取url采集數(shù)據(jù)稽穆,當(dāng)master redis中"shixisheng:start_urls"和"slave_1:requests"都為空時(shí),爬蟲(chóng)會(huì)暫停等待赶撰,直到redis中有新的url舌镶。若再無(wú)新url添加進(jìn)來(lái)柱彻,就可以在此刻結(jié)束程序。
運(yùn)行爬蟲(chóng)
scrapy runspider myspider.py
向redis中裝入url們
redis-cli lpush myspider:start_urls http://xiaowangzhi.com