crapy 去重与 scrapy_redis 去重与 布隆过滤器

目录

  • scrapy 的去重
  • scrapy-redis 去重
  • 自定义过滤
  • 布隆过滤器

在开始介绍 scrapy 的去重之前,先想想我们是怎么对 requests 对去重的。

requests 只是下载器,本身并没有提供去重功能。所以我们需要自己去做。

很典型的做法是事先定义一个去重队列,判断抓取的 url 是否在其中,如

crawled_urls = set()

def check_url(url):
 if url not in crawled_urls:
     return True
 return False

此时的集合是保存在内存中的,随着爬虫抓取内容变多,该集合会越来越大,有什么办法呢?

接着往下看,你会知道的

scrapy 的去重

scrapy 对 request 不做去重很简单,只需要在 request 对象中设置dont_filter为 True,如

yield Request(‘https://www.baidu.com/?w=python‘,dont_filter=True)

看看源码是如何做的

_fingerprint_cache = weakref.WeakKeyDictionary()
def request_fingerprint(request, include_headers=None):
 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]

翻译

返回请求指纹

请求指纹是唯一标识请求指向的资源的哈希。 例如,请使用以下两个网址:

http://www.example.com/query?id=111&cat=222
http://www.example.com/query?cat=222&id=111

即使这两个不同的 URL 都指向相同的资源并且是等价的(即,它们应该返回相同的响应)

另一个例子是用于存储会话 ID 的 cookie。 假设以下页面仅可供经过身份验证的用户访问:

http://www.example.com/members/offers.html

许多网站使用 cookie 来存储会话 ID,这会随机添加字段到 HTTP 请求,因此在计算时应该被忽略指纹。

因此,计算时默认会忽略 request headers。 如果要包含特定 headers,请使用 include_headers 参数,它是要计算 Request headers 的列表

其实就是说:scrapy 使用 sha1 算法,对每一个 request 对象加密,生成 40 位十六进制数

我们看源码,重点是以下三行

     fp = hashlib.sha1()
     fp.update(to_bytes(request.method))
     fp.update(to_bytes(canonicalize_url(request.url)))
     fp.update(request.body or b‘‘)

如果没有自定义 headers,只计算 method、url、和二进制 body,我们来计算下,代码

print(request_fingerprint(scrapy.Request(‘http://www.example.com/query?id=111&cat=222‘)))
print(request_fingerprint(scrapy.Request(‘http://www.example.com/query?cat=222&id=111‘)))
print(request_fingerprint(scrapy.Request(‘http://www.example.com/query‘)))

输出

fad8cefa4d6198af8cb1dcf46add2941b4d32d78
fad8cefa4d6198af8cb1dcf46add2941b4d32d78
b64c43a23f5e8b99e19990ce07b75c295165a923

可以看到第一条和第二条的密码是一样的,是因为调用了canonicalize_url方法

scrapy 的去重默认会保存到内存中,如果任务重启,会导致内存中所有去重队列消失

scrapy-redis 去重

scrapy-redis 重写了 scrapy 的调度器和去重队列,所以需要在 settings 中修改如下两列

# Enables scheduling storing requests queue in redis.
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

一般我们会在 redis 中看到这两个,分别是去重队列和种子链接

源码

 def request_seen(self, request):
     """Returns True if request was already seen.
     Parameters
     ----------
     request : scrapy.http.Request
     Returns
     -------
     bool
     """
     fp = self.request_fingerprint(request)
     # This returns the number of values added, zero if already exists.
     added = self.server.sadd(self.key, fp)
     return added == 0

 def request_fingerprint(self, request):
     """Returns a fingerprint for a given request.
     Parameters
     ----------
     request : scrapy.http.Request
     Returns
     -------
     str
     """
     return request_fingerprint(request)

首先拿到 scrapy.http.Request 会先调用 self.request_fingerprint 去计算,也就是 scrapy 的 sha1 算法去加密,然后会向 redis 中添加该指纹。

该函数的作用是:计算该请求指纹,添加到 redis 的去重队列,如果已经存在该指纹,返回 True。

我们可以看到,只要有在 settings 中添加DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter",就会在 redis 中新加一列去重队列,说下这样做的优劣势:

  1. 优点:将内存中的去重队列序列化到 redis 中,就算爬虫重启或者关闭,也可以再次使用,你可以使用 SCHEDULER_PERSIST 来调整缓存
  2. 缺点:如果你需要去重的指纹过大,redis 占用空间过大。8GB=8589934592Bytes,平均一个去重指纹 40Bytes,约可以存储 214,748,000 个(2 亿)。所以在做关系网络爬虫中,序列化到 redis 中可能并不是很好,保存在内存中也不好,所以就产生了布隆过滤器。

自定义过滤

如果想自定义 Filter,可以通过继承,重写 request_seen

from?scrapy.dupefilter?import?RFPDupeFilter
class?SeenURLFilter(RFPDupeFilter):
??????"""A?dupe?filter?that?considers?the?URL"""
??????def?__init__(self,?path=None):
????????self.urls_seen?=?set()
????????RFPDupeFilter.__init__(self,?path)
??????def?request_seen(self,?request):
????????if?request.url?in?self.urls_seen:
??????????????return?True
????????else:
??????????????self.urls_seen.add(request.url)

#?修改settings设置
DUPEFILTER_CLASS?=‘scraper.custom_filters.SeenURLFilter‘

布隆过滤器

bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫 url 去重)

原理:

BloomFilter 会开辟一个m位的bitArray(位数组),开始所有数据全部置 0 。当一个元素过来时,能过多个哈希函数(h1,h2,h3....)计算不同的在哈希值,并通过哈希值找到对应的bitArray下标处,将里面的值 0 置为 1 。

在验证的时候只需要验证这些比特位是否都是 1 即可,如果其中有一个为 0,那么元素一定不在集合里,如果全为 1,则很可能在集合里。(因为可能会有其它的元素也映射到相应的比特位上)

另外说明一下,当来查找对应的值时,同样通过哈希函数求值,再去寻找数组的下标,如果所有下标都为1时,元素存在。当然也存在错误率。(如:当数组全部为1时,那么查找什么都是存在的),但是这个错误率的大小,取决于数组的位数和哈希函数的个数。

python 中使用布隆过滤器

#python3.6 安装
#需要先安装bitarray
pip3 install bitarray-0.8.1-cp36-cp36m-win_amd64.whl(pybloom_live依赖这个包,需要先安装)
#下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/
pip3 install pybloom_live

示例一

#ScalableBloomFilter 可以自动扩容
from pybloom_live import ScalableBloomFilter

bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)

url = "www.cnblogs.com"
url2 = "www.liuqingzheng.top"

bloom.add(url)

print(url in bloom)
print(url2 in bloom)

示例二

#BloomFilter 是定长的
from pybloom_live import BloomFilter

bf = BloomFilter(capacity=1000)
url=‘www.baidu.com‘
bf.add(url)

print(url in bf)
print("www.liuqingzheng.top" in bf)

用了 布隆过滤器 后,就可以判断 url 是否爬过,从而可以实现增量式爬取

布隆过滤器的结果是存在内存中的,后期可以存进 redis 中

原文地址:https://www.cnblogs.com/kai-/p/12686579.html

时间: 2024-11-05 18:28:21

crapy 去重与 scrapy_redis 去重与 布隆过滤器的相关文章

爬虫5 scrapy框架2 全站爬取cnblogs, scarpy请求传参, 提高爬取效率, 下载中间件, 集成selenium, fake-useragent, 去重源码分析, 布隆过滤器, 分布式爬虫, java等语言概念补充, bilibili爬视频参考

1 全站爬取cnblogs # 1 scrapy startproject cnblogs_crawl # 2 scrapy genspider cnblogs www.cnblogs.com 示例: # cnblogs_crawl/cnblogs_crawl/spiders/cnblogs.py import scrapy from cnblogs_crawl.items import CnblogsCrawlItem from scrapy.http import Request class

网络爬虫:URL去重策略之布隆过滤器(BloomFilter)的使用

前言: 最近被网络爬虫中的去重策略所困扰.使用一些其他的"理想"的去重策略,不过在运行过程中总是会不太听话.不过当我发现了BloomFilter这个东西的时候,的确,这里是我目前找到的最靠谱的一种方法. 如果,你说URL去重嘛,有什么难的.那么你可以看完下面的一些问题再说这句话. 关于BloomFilter: Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员.如果检测

第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中

第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲-将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详解 基本概念 如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定.链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢.不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构.它可以通过一

海量数据处理利器之布隆过滤器

看见了海量数据去重,找到停留时间最长的IP等问题,有博友提到了Bloom Filter,我就查了查,不过首先想到的是大叔,下面就先看看大叔的风采. 一.布隆过滤器概念引入 (Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的.它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中.它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False positives,即Bloom

深入Redis(五)布隆过滤器

布隆过滤器 HyperLogLog可以进行估数,非常具有价值,可以解决很多精确度要求不高的统计需求. 但是如果我们想知道某一值是不是在HyperLogLog结构内则无能为力了,因为HyperLogLog没有提供类似pfcontains这种方法. 比如,我们在使用新闻客户端看新闻时,它会不断推荐新的内容,每次推荐时都要去重,那么如何实现推送去重? 我们会想到,服务器记录了用户看过的所有历史纪录,推荐系统每次都从用户的历史纪录内筛选已经看过的记录,但用户量很大并且每个用户看过的新闻又很多时,推荐系统

布隆过滤器的方式解决缓存穿透问题

1.原理 布隆过滤器的巨大用处就是,能够迅速判断一个元素是否在一个集合中.因此他有如下三个使用场景: 网页爬虫对URL的去重,避免爬取相同的URL地址 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信) 缓存穿透,将所有可能存在的数据缓存放到布隆过滤器中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉. OK,接下来我们来谈谈布隆过滤器的原理 其内部维护一个全为0的bit数组,需要说明的是,布隆过滤器有一个误判率的概念,误判率越低,则数组越长,所占空间越大.误判率越

从另一个角度看大数据量处理利器 布隆过滤器

思路:从简单的排序谈到BitMap算法,再谈到数据去重问题,谈到大数据量处理利器:布隆过滤器. 情景1:对无重复的数据进行排序 @给定数据(2,4,1,12,9,7,6)如何对它排序? 方法1:基本的排序方法包括冒泡,快排等. 方法2:使用BitMap算法 方法1就不介绍了,方法2中所谓的BitMap是一个位数组,跟平时使用的数组的唯一差别在于操作的是位. 首先是开辟2个字节大小的位数组,长度为16(该长度由上述数据中最大的数字12决定的)如图         然后,读取数据,2存放在位数组中下

布隆过滤器:高效、大概的判断数据是否存在

1      什么是布隆过滤器 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,或者说“判断一个元素是否存在一个集合中”,比如: 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 在网络爬虫里,一个网址是否被访问过 yahoo, gmail等邮箱垃圾邮件过滤功能 相比于传统的 List.Set.Map 等

Spark布隆过滤器(bloomFilter)

数据过滤在很多场景都会应用到,特别是在大数据环境下.在数据量很大的场景实现过滤或者全局去重,需要存储的数据量和计算代价是非常庞大的.很多小伙伴第一念头肯定会想到布隆过滤器,有一定的精度损失,但是存储性能和计算性能可以达到几何级别的提升.很多第三方框架也实现了相应的功能,比如hbase框架实现的布隆过滤器性能是非常的棒,redis也可以实现相应的功能.这些需要借助于第三方框架,需要维护第三方框架.如果公司没有部署相应架构,单独为使用布隆过滤器部署一套集群,代价还是非常大的. 我们在做流式计算时需要