5.scrapy过滤器
scrapy过滤器
1. 过滤器
当我们在爬取网页的时候可能会遇到一个调转连接会在不同页面出现,这个时候如果我们的爬虫程序不能识别出
该链接是已经爬取过的话,就会造成一种重复不必要的爬取。所以我们要对我们即将要爬取的网页进行过滤,把重
复的网页链接过滤掉。
2. 指纹过滤器
去重处理可以避免将重复性的数据保存到数据库中以造成大量的冗余性数据。不要在获得爬虫的结果后进行内容过
滤,这样做只不过是避免后端数据库出现重复数据。
去重处理对于一次性爬取是有效的,但对于增量式爬网则恰恰相反。对于持续性长的增量式爬网,应该进行"前置过
滤",这样可以有效地减少爬虫出动的次数。在发出请求之前检查询爬虫是否曾爬取过该URL,如果已爬取过,则让爬
虫直接跳过该请求以避免重复出动爬虫。
Scrapy 提供了一个很好的请求指纹过滤器(Request Fingerprint duplicates filter)
scrapy.dupefilters.ReppupeFilter ,当它被启用后,会自动记录所有成功返回响应的请求的URL,并将其以文件
(requests.seen)方式保存在项目目录中。请求指纹过滤器的原理是为每个URL生成一个指纹并记录下来,一旦
当前请求的URL在指纹库中有记录,就自动跳过该请求。
默认情况下这个过滤器是自动启用的,当然也可以根据自身的需求编写自定义的过滤器。
默认过滤器开启的地方:
def start_requests(self):
for url in self.start_urls:
# 每一个url封装成Request对象,交给调度器
# 这里的dont_filter=True 就是默认开启scrapy自带的过滤器
yield Request(url, dont_filter=True)
2.1 过滤器源码
2.1.1 基本过滤器
这是一个基类,scrapy自带的指纹过滤器就是继承这个类,然后重写这些方法实现的。
如果你经常看源码的话,你话发现很多功能的实现都有一个最基础的基类,然后实现功能的那个类,继承它
并对他的方法进行重写。scrapy框架中就是这样的。
class BaseDupeFilter:
# 基本的过滤器
@classmethod
def from_settings(cls, settings):
"""这个方法可以从settings.py中获取数据"""
return cls()
def request_seen(self, request):
# 对 request 去重的方法
return False
def open(self): # can return deferred 爬虫打开的时候
pass
def close(self, reason): # can return a deferred 爬虫关闭的时候
pass
def log(self, request, spider): # log that a request has been filtered 爬虫的日志
pass
2.2.2 默认的过滤器
继承BaseDupeFilter类,然后对内部重写。
class RFPDupeFilter(BaseDupeFilter):
"""Request Fingerprint duplicates filter 指纹过滤器 对整个request的去重"""
# 默认的话是一个指纹过滤器, 会对整个request对象进行过滤 (url/method/params...)
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) # 更新文件
@classmethod
def from_settings(cls, settings):
# 从配置文件中取到要应用的过滤器。
debug = settings.getbool('DUPEFILTER_DEBUG')
return cls(job_dir(settings), debug)
def request_seen(self, request):
# 拿到request 传到 request_fingerprint方法 摘要出来的指纹 fp
fp = self.request_fingerprint(request)
# 如果指纹在集合中
if fp in self.fingerprints:
# 返回 True
return True
# 不在就追加到集合
self.fingerprints.add(fp)
if self.file:
# 指纹写入到文件
self.file.write(fp + '\n')
def request_fingerprint(self, request):
# 返回请求生成的指纹
return request_fingerprint(request)
def close(self, reason):
# 爬虫结束关闭存放指纹的文件
if self.file:
self.file.close()
def log(self, request, spider):
# 爬虫日志
if self.debug:
msg = "Filtered duplicate request: %(request)s (referer: %(referer)s)"
args = {'request': request, 'referer': referer_str(request)}
self.logger.debug(msg, args, extra={'spider': spider})
elif self.logdupes:
msg = ("Filtered duplicate request: %(request)s"
" - no more duplicates will be shown"
" (see DUPEFILTER_DEBUG to show all duplicates)")
self.logger.debug(msg, {'request': request}, extra={'spider': spider})
self.logdupes = False
spider.crawler.stats.inc_value('dupefilter/filtered', spider=spider)
执行流程:
细心的小伙伴可能发现了,这个和pipeline类源码的执行流程差不多,对没错就是差不多。
- 执行 from_settings(cls, settings): 这个类方法
- 执行 _ _init _ _ (self, path=None, debug=False): 实例化对象
- 执行 request_seen(self, request): 拿到由请求生产的指纹
- 执行 log(self, request, spider): 记录日志
- 执行 close(self, reason): 关闭记录指纹的文件
由于 scrapy.dupefilters.RFPDupeFilter 采用文件方式保存指纹库,对于增量爬取且只用于短期运行的项目还能
应对。一旦遇到爬取量巨大的场景时,这个过滤器就显得不太适用了,因为指纹库文件会变得越来越大,过滤器在启动时会一次性将指纹库中所有的URL读入,导致消耗大量内存。
所以我们情况下,在使用scrapy过滤器的时候,都是自己重新自定义。
3. 自定义过滤器
虽然自带的过滤器不好用,但是我们可以用Scrapy提供的 request_fingerprint 函数为请求生成指纹,然后将
指纹写入内存,这样会从内存中存取数据会很快。然后写好的这个类,位置可以随便放,但是一定要在settings.py
文件中从新指定过滤器。
# 这里是放在了当前项目的中间件里面了.
DUPEFILTER_CLASS = 'qd_04_english.middlewares.URLFilter'
# 过滤器先启动,再执行爬虫
import hashlib
from scrapy.dupefilters import BaseDupeFilter
class URLFilter(BaseDupeFilter):
"""根据URL过滤"""
@classmethod
def from_settings(cls, settings):
# 从settings里面取到配置文件
debug = settings.getbool('DUPEFILTER_DEBUG')
return cls()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 过滤url的集合
self.url_set = set()
def request_seen(self, request):
"""对每一个请求进行过滤"""
url = self.request_fingerprint(request)
if url in self.url_set:
# 返回True就代表这个url已经被请求过了
return True
else:
self.url_set.add(request.url)
def request_fingerprint(self, request):
# 返回由url摘要后的字符串
return hashlib.md5(request.url.encode()).hexdigest()
注意:start_urls 中的请求,默认是不过滤的。
4. 总结
之前我们在管道中,讲到的数据去重,是对结果的去重,这里我们讲的过滤是对请求的去重。
一定一定要会看源码,会自定义一些组件。因为自带的公司一般都不会用的,因为不好用。
5.scrapy过滤器的更多相关文章
- 第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中
第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详 ...
- 三十七 Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中
Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详解 基本概念 如 ...
- Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?
使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...
- scrapy2_初窥Scrapy
递归知识:oop,xpath,jsp,items,pipline等专业网络知识,初级水平并不是很scrapy,可以从简单模块自己写. 初窥Scrapy Scrapy是一个为了爬取网站数据,提取结构性数 ...
- Python爬虫从入门到放弃(十六)之 Scrapy框架中Item Pipeline用法
当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...
- 三、scrapy后续
CrawlSpiders 通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl tencent tencent.com 我们通过正则表达 ...
- Scrapy爬虫框架第三讲(linux环境)
下面我们来学习下Spider的具体使用: 我们已上节的百度阅读爬虫为例来进行分析: 1 # -*- coding: utf-8 -*- 2 import scrapy 3 from scrapy.li ...
- scrapy分布式爬虫scrapy_redis一篇
分布式爬虫原理 首先我们来看一下scrapy的单机架构: 可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页 ...
- Scrapy爬虫框架(实战篇)【Scrapy框架对接Splash抓取javaScript动态渲染页面】
(1).前言 动态页面:HTML文档中的部分是由客户端运行JS脚本生成的,即服务器生成部分HTML文档内容,其余的再由客户端生成 静态页面:整个HTML文档是在服务器端生成的,即服务器生成好了,再发送 ...
随机推荐
- CGLIB动态代理机制,各个方面都有写到
CGLIB库介绍 代理提供了一个可扩展的机制来控制被代理对象的访问,其实说白了就是在对象访问的时候加了一层封装.JDK从1.3版本起就提供了一个动态代理,它使用起来非常简单,但是有个明显的缺点:需要目 ...
- React 为什么要把事件挂载到 document 上 & 事件机制源码分析
前言 我们都知道 React 组件绑定事件的本质是代理到 document 上,然而面试被问到,为什么要这么设计,有什么好处吗? 我知道肯定不会是因为虚拟 DOM 的原因,因为 Vue 的事件就能挂载 ...
- WPF 学习(一)
一.WPF介绍 WPF全称 Windows Presentation Foundation,干啥用的? 主要是用来制作Windows桌面客户端软件的. .Net平台下制作Windows桌面客户端软件主 ...
- 如何从Debian 9 Stretch升级到Debian10 Buster
Let's first fully upgrade our current Debian Stretch system: # apt-get update # apt-get upgrade # ap ...
- openshift v1.5 不能登录system:admin 问题
这个好像是无法解决的,我按照github和google所有步骤试过了,还是不能登录system:admin .需要密码.弄了两个小时,后来放弃了,直接使用新版本3.6的,按照官网方法部署,可以使用直接 ...
- 别再写一摞if-else了!再写开除!两种设计模式带你消灭它!
代码洁癖狂们!看到一个类中有几十个if-else是不是很抓狂? 设计模式学了用不上吗?面试的时候问你,你只能回答最简单的单例模式,问你有没有用过反射之类的高级特性,回答也是否吗? 这次就让设计模式(模 ...
- Python绘图之Turtle库详解(1)
Turtle库是Python语言中一个很流行的绘制图像的函数库,想象一个小乌龟,在一个横轴为x.纵轴为y的坐标系原点,(0,0)位置开始,它根据一组函数指令的控制,在这个平面坐标系中移动,从而在它爬行 ...
- MyBatis 实体类属性与表字段不一致
原文链接:https://blog.csdn.net/zx48822821/java/article/details/79050735 因为数据库一般设置为表的字段不区分大小写,所以数据库中表的字段通 ...
- ImportError: cannot import name _remove_dead_weakref
出现这个错误, 和python环境有关. 电脑有多个版本造成的. python3 有这个_remove_dead_weakref python 2.7.10 并没有_remove_dead_weakr ...
- MongoDB文档(二)--查询
(一)查询文档 查询文档可以使用以下方法 # 以非结构化的方式显示所有的文档 db.<collectionName>.find(document) # 以结构化的方式显示所有文档 db.& ...