scrapy_redis种子优化
前言:
繼《scrapy_redis去重優(yōu)化(已有7億條數(shù)據(jù)),附Demo福利》優(yōu)化完去重之后,Redis的內(nèi)存消耗降了許多,然而還不滿足。這次對scrapy_redis的種子隊(duì)列作了一些優(yōu)化(嚴(yán)格來說并不能用上“優(yōu)化”這詞,其實(shí)就是結(jié)合自己的項(xiàng)目作了一些改進(jìn),對本項(xiàng)目能稱作優(yōu)化,對scrapy_redis未必是個優(yōu)化)。
scrapy_redis默認(rèn)是將Request對象序列化后(變成一條字符串)存入Redis作為種子,需要的時候再取出來進(jìn)行反序列化,還原成一個Request對象。
現(xiàn)在的問題是:序列化后的字符串太長,短則幾百個字符,長則上千。我的爬蟲平時至少也要維護(hù)包含幾千萬種子的種子隊(duì)列,占用內(nèi)存在20G~50G之間(Centos)。想要縮減種子的長度,這樣不僅Redis的內(nèi)存消耗會降低,各個slaver從Redis拿種子的速度也會有所提高,從而整個分布式爬蟲系統(tǒng)的抓取速度也會有所提高(效果視具體情況而定,要看爬蟲主要阻塞在哪里)。
記錄:
1、首先看調(diào)度器,即scrapy_redis模塊下的scheduler.py文件,可以看到enqueue_request()方法和next_request()方法就是種子入隊(duì)列和出隊(duì)列的地方,self.queue指的是我們在setting.py里面設(shè)定的SCHEDULER_QUEUE_CLASS值,常用的是'scrapy_redis.queue.SpiderPriorityQueue'。
2、進(jìn)入scrapy模塊下的queue.py文件,SpiderPriorityQueue類的代碼如下:
可以看到,上面用到了Redis的zset數(shù)據(jù)結(jié)構(gòu)(它可以給種子加優(yōu)先級),在進(jìn)Redis之前用_encode_request()方法將Request對象轉(zhuǎn)成字符串,_encode_request()和_decode_request是Base類下面的兩個方法:
def _encode_request(self, request):"""Encode a request object"""return pickle.dumps(request_to_dict(request, self.spider), protocol=-1)def _decode_request(self, encoded_request):"""Decode an request previously encoded"""return request_from_dict(pickle.loads(encoded_request), self.spider)可以看到,這里先將Request對象轉(zhuǎn)成一個字典,再將字典序列化成一個字符串。Request對象怎么轉(zhuǎn)成一個字典呢?看下面的代碼,一目了然。
def request_to_dict(request, spider=None):"""Convert Request object to a dict.If a spider is given, it will try to find out the name of the spider methodused in the callback and store that as the callback."""cb = request.callbackif callable(cb):cb = _find_method(spider, cb)eb = request.errbackif callable(eb):eb = _find_method(spider, eb)d = {'url': to_unicode(request.url), # urls should be safe (safe_string_url)'callback': cb,'errback': eb,'method': request.method,'headers': dict(request.headers),'body': request.body,'cookies': request.cookies,'meta': request.meta,'_encoding': request._encoding,'priority': request.priority,'dont_filter': request.dont_filter,}return d
注:d為Request對象轉(zhuǎn)過來的字典,data為字典序列化后的字符串。
3、了解完scrapy_redis默認(rèn)的種子處理方式,現(xiàn)在針對自己的項(xiàng)目作一些調(diào)整。我的是一個全網(wǎng)爬蟲,每個種子需要記錄的信息主要有兩個:url和callback函數(shù)名。此時我們選擇不用序列化,直接用簡單粗暴的方式,將callback函數(shù)名和url拼接成一條字符串作為一條種子,這樣種子的長度至少會減少一半。另外我們的種子并不需要設(shè)優(yōu)先級,所以也不用zset了,改用Redis的list。以下是我新建的SpiderSimpleQueue類,加在queue.py中。如果在settings.py里將SCHEDULER_QUEUE_CLASS值設(shè)置成'scrapy_redis.queue.SpiderSimpleQueue'即可使用我這種野蠻粗暴的種子。
4、另外需要提醒的是,如果scrapy中加了中間件process_request(),當(dāng)yield一個Request對象的時候,scrapy_redis會直接將它丟進(jìn)Redis種子隊(duì)列,未執(zhí)行process_requset();需要一個Request對象的時候,scrapy_redis會從Redis隊(duì)列中取出種子,此時才會處理process_request()方法,接著去抓取網(wǎng)頁。
所以并不需要擔(dān)心process_request()里面添加的Cookie在Redis中放太久會失效,因?yàn)檫M(jìn)Redis的時候它壓根都還沒執(zhí)行process_request()。事實(shí)上Request對象序列化的時候帶上的字段很多都是沒用的默認(rèn)字段,很多爬蟲都可以用 “callback+url” 的方式來優(yōu)化種子。
5、最后,在Scrapy_Redis_Bloomfilter(Github傳送門)這個demo中我已作了修改,大家可以試試效果。
結(jié)語:
經(jīng)過以上優(yōu)化,Redis的內(nèi)存消耗從42G降到了27G!里面包含7億多條種子的去重?cái)?shù)據(jù) 和4000W+條種子。并且六臺子爬蟲的抓取速度都提升了一些。
兩次優(yōu)化,內(nèi)存消耗從160G+降到現(xiàn)在的27G,效果也是讓人滿意!
轉(zhuǎn)載請注明出處,謝謝!(原文鏈接:http://blog.csdn.net/bone_ace/article/details/53306629)
總結(jié)
以上是生活随笔為你收集整理的scrapy_redis种子优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: angular复用路由组件_Angula
- 下一篇: map映射的基础用法