|
|
在开始介绍scrapy的去重之前,先想想我们是怎么对requests对去重的。requests只是下载器,本身并没有提供去重功能。所以我们需要自己去做。很典型的做法是事先定义一个去重队列,判断抓取的url是否在其中,如下:
|
|
此时的集合是保存在内存中的,随着爬虫抓取内容变多,该集合会越来越大,有什么办法呢?
接着往下看,你会知道的。
scrapy的去重
scrapy对request不做去重很简单,只需要在request对象中设置dont_filter
为True,如
|
|
看看源码是如何做的,位置
|
|
注释过多,我就删掉了。谷歌翻译 + 人翻
|
|
其实就是说:scrapy使用sha1算法,对每一个request对象加密,生成40为十六进制数,如:’fad8cefa4d6198af8cb1dcf46add2941b4d32d78’。
我们看源码,重点是一下三行
|
|
如果没有自定义headers,只计算method、url、和二进制body,我们来计算下,代码:
|
|
输出:
|
|
可以看到第一条和第二条的密码是一样的,是因为调用了canonicalize_url
方法,该方法返回如下
|
|
scrapy的去重默认会保存到内存中,如果任务重启,会导致内存中所有去重队列消失
scrapy-redis的去重
scrapy-redis重写了scrapy的调度器和去重队列,所以需要在settings中修改如下两列
|
|
一般我们会在redis中看到这两个,分别是去重队列和种子链接
先看看代码:重要代码
|
|
首先拿到scrapy.http.Request会先调用self.request_fingerprint去计算,也就是scrapy的sha1算法去加密,然后会向redis中添加该指纹。
该函数的作用是:计算该请求指纹,添加到redis的去重队列,如果已经存在该指纹,返回True。
我们可以看到,只要有在settings中添加DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
,就会在redis中新加一列去重队列,说下这样做的优劣势:
- 优点:将内存中的去重队列序列化到redis中,及时爬虫重启或者关闭,也可以再次使用,你可以使用SCHEDULER_PERSIST来调整缓存
- 缺点:如果你需要去重的指纹过大,redis占用空间过大。8GB=8589934592Bytes,平均一个去重指纹40Bytes,约可以存储214,748,000个(2亿)。所以在做关系网络爬虫中,序列化到redis中可能并不是很好,保存在内存中也不好,所以就产生了布隆过滤器。
布隆过滤器
它的原理是将一个元素通过 k 个哈希函数,将元素映射为 k 个比特位,在 bitmap 中把它们置为 1。在验证的时候只需要验证这些比特位是否都是 1 即可,如果其中有一个为 0,那么元素一定不在集合里,如果全为 1,则很可能在集合里。(因为可能会有其它的元素也映射到相应的比特位上)
同时这也导致不能从 Bloom filter 中删除某个元素,无法确定这个元素一定在集合中。以及带来了误报的问题,当里面的数据越来越多,这个可能在集合中的靠谱程度就越来越低。(由于哈希碰撞,可能导致把不属于集合内的元素认为属于该集合)
布隆过滤器的缺点是错判,就是说,不在的一定不在,在的不一定在,而且无法删除其中数据。
|
|
python3使用pybloomfilter的例子。
那么如何在scrapy中使用布隆过滤器呢,崔大大已经写好了,地址:ScrapyRedisBloomFilter,已经打包好,可以直接安装
|
|
在settings中这样配置:
|
|
其实也是修改了调度器与去重方法,有兴趣的可以了解下。