scrapy-redis所實現的兩種分布式:爬蟲分布式以及item處理分布式。分別是由模塊scheduler和模塊pipelines實現。
壹、Scrapy-redis各個組件介紹
(I) connection.py
負責根據setting中配置實例化redis連接。被dupefilter和scheduler調用,總之涉及到redis存取的都要使用到這個模塊。
(II) dupefilter.py
負責執行requst的去重,實現的很有技巧性,使用redis的set數據結構。但是註意scheduler並不使用其中用於在這個模塊中實現的dupefilter鍵做request的調度,而是使用queue.py模塊中實現的queue。
當request不重復時,將其存入到queue中,調度時將其彈出。
(III)queue.py
其作用如II所述,但是這裏實現了三種方式的queue:
FIFO的SpiderQueue,SpiderPriorityQueue,以及LIFI的SpiderStack。默認使用的是第二中,這也就是出現之前文章中所分析情況的原因(鏈接)。
(IV)pipelines.py
這是是用來實現分布式處理的作用。它將Item存儲在redis中以實現分布式處理。
另外可以發現,同樣是編寫pipelines,在這裏的編碼實現不同於文章(鏈接:)中所分析的情況,由於在這裏需要讀取配置,所以就用到了from_crawler()函數。
(V)scheduler.py
此擴展是對scrapy中自帶的scheduler的替代(在settings的SCHEDULER變量中指出),正是利用此擴展實現crawler的分布式調度。其利用的數據結構來自於queue中實現的數據結構。
scrapy-redis所實現的兩種分布式:爬蟲分布式以及item處理分布式就是由模塊scheduler和模塊pipelines實現。上述其它模塊作為為二者輔助的功能模塊。
(VI)spider.py
設計的這個spider從redis中讀取要爬的url,然後執行爬取,若爬取過程中返回更多的url,那麽繼續進行直至所有的request完成。之後繼續從redis中讀取url,循環這個過程。
二、組件之間的關系
三、scrapy-redis實例分析
(1)?spiders/ ebay_redis.py
classEbayCrawler(RedisMixin,CrawlSpider):
"""Spiderthat reads urls from redis queue (mycrawler:start_urls)."""
name = 'ebay_redis'
redis_key = ' ebay_redis:start_urls'
rules = (
# follow all links
# Rule(SgmlLinkExtractor(),callback='parse_page', follow=True),
Rule(sle(allow=('[^\s]+/itm/', )), callback='parse_item'),
)
#該方法是最關鍵的方法,該方法名以下劃線開頭,建立了和redis的關系
def _set_crawler(self, crawler):
CrawlSpider._set_crawler(self, crawler)
RedisMixin.setup_redis(self)
# 解析sku頁面
defparse_item(self,response):
sel =Selector(response)
base_url =get_base_url(response)
item = EbayphoneItem()
print base_url
item['baseurl'] =[base_url]
item['goodsname'] =sel.xpath("//h1[@id='itemTitle']/text()").extract()
return item
該類繼承了RedisMixin(scrapy_redis/spiders.py中的壹個類)和CrawlSpider,加載配置文件的各項,建立和redis的關聯,同時進行抓取後的解析。關鍵方法為_set_crawler(self, crawler),關鍵屬性是redis_key,該key如果沒有初始化則默認為spider.name:start_urls
_set_crawler()方法是如何被調用的:
scrapy/crawl.py/Crawler:?crawl() ->
scrapy/crawl.py/Crawler:_create_spider () ->
CrawlSpider:from_crawler() –>
scrapy/spiders/Spider:?from_crawler() ->
ebay_redis.py :_set_crawler()
(2)?setting.py
SPIDER_MODULES= ['example.spiders']
NEWSPIDER_MODULE= 'example.spiders'
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline':300,
#通過配置下面該項RedisPipeline'會將item寫入key為
#spider.name:items的redis的list中,供後面的分布式處理item
'scrapy_redis.pipelines.RedisPipeline':400,
}
SCHEDULER= "scrapy_redis.scheduler.Scheduler"
#不清理redisqueues, 允許暫停或重啟crawls
SCHEDULER_PERSIST= True
SCHEDULER_QUEUE_CLASS= 'scrapy_redis.queue.SpiderPriorityQueue'
#該項僅對queueclass is SpiderQueue or SpiderStack生效,阻止spider被關閉的最大空閑時間
SCHEDULER_IDLE_BEFORE_CLOSE= 10
#連接redis使用
REDIS_HOST = '123.56.184.53'
REDIS_PORT= 6379
(3)?process_items.py:
defmain():
pool =redis.ConnectionPool(host='123.56.184.53', port=6379, db=0)
r = redis.Redis(connection_pool=pool)
while True:
# process queue as FIFO, change `blpop`to `brpop` to process as LIFO
source, data =r.blpop(["ebay_redis:items"])
item = json.loads(data)
try:
print u"Processing: %(name)s<%(link)s>" % item
except KeyError:
print u"Error procesing:%r" % item
if__name__ == '__main__':
main()
該模塊是從redis對應的list中取出item,進行處理,可以運行多個進程分布式處理items
(4)執行過程如下:
首先在redis服務器端打開redis服務:
./redis-server
其次執行
./redis-cli lpush ebaycrawler:start_urls?/sch/Cell-Phones-Smartphones-/9355/i.html
然後運行爬蟲:
scrapy runspiderebay_redis.py
可以執行多個爬蟲,同時對ebay_redis:start_urls中的url進行分布式爬取,爬取後的結果都存入了ebay_redis:items的list中,供後續再次處理
最後可以查看items隊列中的內容
./redis-cli llen ebay_redis:items 可以看到該items中總的個數