笔记-scrapy-去重

笔记-scrapy-去重

 

1.      scrapy 去重

scrapy 版本:1.5.0

 

第一步是要找到去重的代码,scrapy在请求入列前去重,具体源码在scheduler.py:

 

    def enqueue_request(self, request):

        if not request.dont_filter and self.df.request_seen(request):

            self.df.log(request, self.spider)

            return False

        dqok = self._dqpush(request)

        if dqok:

            self.stats.inc_value('scheduler/enqueued/disk', spider=self.spider)

        else:

            self._mqpush(request)

            self.stats.inc_value('scheduler/enqueued/memory', spider=self.spider)

        self.stats.inc_value('scheduler/enqueued', spider=self.spider)

        return True

红色部分进行去重:

dont_filter在requests类中有提到,用于指定是否对请求进行去重

 

self.df.request_seen(request)是什么?

self.df = dupefilter,而dupefilter由dupefilter_cls = load_object(settings['DUPEFILTER_CLASS'])指定

文档settings中有提到DUPEFILTER_CLASS

Default: 'scrapy.dupefilters.RFPDupeFilter'

 

找到scrapy.dupefilters.RFPDupeFilter,其中有request_seen()

    def request_seen(self, request):

        fp = self.request_fingerprint(request)

        if fp in self.fingerprints:

            return True

        self.fingerprints.add(fp)

        if self.file:

            self.file.write(fp + os.linesep)

简单来说是通过self.request_fingerprint(request)得到fp,判断是否存在于self.fingerprints中,如果存在,则返回True,否则返回None。

self.fingerprints=set() 它是一个指纹集合。

 

self.request_fingerprint()是什么?继续找

self.request_fingerprint()实际是调用自utils.request.py的request_fingerprint()

源码如下:

def request_fingerprint(request, include_headers=None):

    """

    Return the request fingerprint.

    """

    if include_headers:

        include_headers = tuple(to_bytes(h.lower())

                                 for h in sorted(include_headers))

    cache = _fingerprint_cache.setdefault(request, {})

    if include_headers not in cache:

        fp = hashlib.sha1()

        fp.update(to_bytes(request.method))

        fp.update(to_bytes(canonicalize_url(request.url)))

        fp.update(request.body or b'')

        if include_headers:

            for hdr in include_headers:

                if hdr in request.headers:

                    fp.update(hdr)

                    for v in request.headers.getlist(hdr):

                        fp.update(v)

        cache[include_headers] = fp.hexdigest()

    return cache[include_headers]

 

有点杂,它返回的是request的伪hash码,它对request的method,处理后的url,body进行hash,返回hash值。

 

1.1.    总结

  1. 去重是在请求入队列之前进行的,dont_filter控制是否进行去重(默认为false去重);
  2. 通过request_seen()判断是否重复,实际过程是计算出request的指纹,去集合中匹配是否存在,;
  3. 使用request_fingerprint计算请求的指纹。

 

从代码中可以看到去重依赖的是set,这样的话在爬虫中断后会被清空,scrapy提供了一个方案:

将指纹保存到文件中,每次实例化时从文件中读取。

    def __init__(self, path=None, debug=False):

        self.file = None

        self.fingerprints = set()

        self.logdupes = True

        self.debug = debug

        self.logger = logging.getLogger(__name__)

        if path:

            self.file = open(os.path.join(path, 'requests.seen'), 'a+')

            self.file.seek(0)

            self.fingerprints.update(x.rstrip() for x in self.file)

 

posted @ 2018-11-08 17:02  木林森__𣛧  阅读(244)  评论(0)    收藏  举报