思路
雖然scrapy支持多線程型型,但是單機scrapy也是有性能瓶頸的衙耕。使用scrapy-redis可以將scrapy改造成分布式的爬蟲架構(gòu)吩愧。
改造的原理是什么寇甸?
對于原版的scrapy缤沦,scrapy-redis修改了四個部分:調(diào)度器(Scheduler)虎韵、去重(Dupefilter)、管道(Pipeline)缸废、抓取器(Spider)包蓝。其核心是使用了redis代替了原本的queue,因為redis可以作為一個消息隊列企量,這樣多個爬蟲實例就可以通過共享消息隊列來實現(xiàn)分布式了测萎。
要實現(xiàn)分布式主要要解決的問題無非就是幾個:
- 任務(wù)分發(fā),讓每個實例知道自己要做什么届巩。
- 共享消息隊列硅瞧,不能出現(xiàn)重復(fù)爬取的情況。
這里Scheduler就是用來分發(fā)任務(wù)的恕汇;如果配置了去重腕唧,Dupefilter會負責(zé)維護一個集合,包含抓取過的url瘾英,避免重復(fù)抓仍娼印;Pipeline可以是自己寫的缺谴,也可以使用RedisPipeline將結(jié)果存儲在redis中但惶;最后Spider中需要實現(xiàn)具體爬蟲功能的部分,也是寫代碼的地方。
如果一切順利膀曾,你可以看到Redis中會出現(xiàn)三個序列:
Redis中的key都是按照spidername:word
的形式來命名的片拍,其中Dupefiler
用來去重,需要合適的配置項妓肢。Requests
包含了所有爬取的序列捌省,是所有各個爬蟲節(jié)點的任務(wù)來源;同時所有在爬取過程中yield出的新request都會被放入這個序列碉钠。最后如果你在配置項中使用了RedisPipeline纲缓,Spider返回的結(jié)果會被存儲在Items
中。
除了這三個序列喊废,有時候會有一個名為start_urls
序列祝高。如果你的爬蟲只寫了處理一個URL時的代碼,但是沒有其實URL污筷,也就是說爬蟲不知道從哪個url開始入手(有了第一個URL之后就會像蛛網(wǎng)一樣衍生出更多的URL)工闺。這時候你會需要用到redis-cli
(redis的客戶端程序)來手動向redis中添加一條url:
redis-cli -h redis_addr -p redis_port -n redis_db# lpush spidername:start_urls CustomURL
這個項目里我把start_urls直接寫在代碼里了。
怎么部署這個分布式爬蟲瓣蛀?
scrapy本身支持并發(fā)和異步IO陆蟆,通過修改其配置項CONCURRENT_REQUESTS
來實現(xiàn),默認為16惋增。因為GIL的存在叠殷,如果配置合理,此時的爬蟲會占滿你一個CPU核心诈皿;通過手動執(zhí)行N個爬蟲實例(scrapy crawl comicCrawler
)林束,N一般是你的CPU核心數(shù),你可以充分發(fā)揮出一臺機器的性能稽亏。同樣壶冒,在其他機器上,你可以執(zhí)行相同的操作截歉,所有的爬蟲實例都共享一個redis數(shù)據(jù)庫哼勇。通過這種多進程+多線程的模式诵闭,理論上可以最大限度地發(fā)揮機器的能力引镊。
具體怎么做桂塞?
這里只包含了從scrapy轉(zhuǎn)scrapy-redis需要修改的地方。
修改Setting.py
在官網(wǎng)上提供了一個scrapy-redis的完整配置文件凉逛,可以按需修改性宏,一般來說,有幾個是必須的状飞,你需要在原本的Setting.py的基礎(chǔ)上毫胜,增加:
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER_PERSIST = True
REDIS_HOST = '34.229.250.31'
REDIS_PORT = 6379
REDIS_PARAMS = {
'db': 11
}
LOG_LEVEL = 'DEBUG'
幾個比較重要參數(shù)是:
- SCHEDULER`是核心部分书斜,scrapy-redis重寫了調(diào)度器用于管理分布式架構(gòu)。
-
DUPEFILTER_CLASS
用用于去重酵使。 -
SCHEDULER_PERSIST
允許配置是否可以暫停\繼續(xù)爬蟲荐吉,互有利弊。 -
SCHEDULER_QUEUE_CLASS
是隊列的類型口渔,有三種:PriorityQueue
(默認),FifoQueue
和LifoQueue
样屠。 -
REDIS
系列:Redis有幾種配置方法,可以使用REDIS_HOST
+REDIS_PORT
組合缺脉,也可使用REDIS_URL
痪欲,也可以通過REDIS_PARAMS
字典對具體的某個參數(shù)做調(diào)整,比如說攻礼,例子里业踢,我選擇使用redis數(shù)據(jù)庫中的第11個實例,避免與其他爬蟲沖突礁扮。 - 其他的一般保持默認即可知举。
除了新增的幾個配置項,如果你選擇將最終的結(jié)果存入redis太伊,你需要向ITEM_PIPELINES
中添加scrapy_redis.pipelines.RedisPipeline
管道雇锡。
ITEM_PIPELINES = {
'comicspider.pipelines.ComicspiderPipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400
}
修改spider文件
所有涉及到的spider文件都需要修改,scarpy的spider類一般繼承于scrapy.Spider
倦畅,這里要調(diào)整為scrapy_redis.spiders
遮糖。然后,沒了叠赐。
這里沒了
是我這個例子中,不需要修改其他代碼了屡江。但是每個人可能寫的spider的結(jié)構(gòu)不一樣芭概,因此調(diào)整起來可能還有一點出入。
比如說:
- 在我的例子中惩嘉,我沒有使用scrapy的
Rules
部件罢洲,因此在多頁面爬取的時候,我需要為每一類請求都指定好合適的callback文黎。你可以通過配置好Rules
惹苗,它可以通過匹配請求的URL,然后給每一類URL綁定相應(yīng)的回調(diào)函數(shù)耸峭。 - 如果你沒有在代碼中配置
start_urls
桩蓉,你需要指定一個redis的key值用來之后向其中推送請求,redis_key='comicCrawler:start_urls'
劳闹。
代碼和效果
代碼我放在GitHub上了院究,我的筆記本是八核的洽瞬,然后再加一個云主機,我一共起了9個實例业汰,每個實例的并發(fā)數(shù)是16伙窃。最后跑出來,大概一個小時可以抓到27W結(jié)果样漆。