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类源码的执行流程差不多,对没错就是差不多。

  1. 执行 from_settings(cls, settings): 这个类方法
  2. 执行 _ _init _ _ (self, path=None, debug=False): 实例化对象
  3. 执行 request_seen(self, request): 拿到由请求生产的指纹
  4. 执行 log(self, request, spider): 记录日志
  5. 执行 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过滤器的更多相关文章

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

    第三百五十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详 ...

  2. 三十七 Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中

    Python分布式爬虫打造搜索引擎Scrapy精讲—将bloomfilter(布隆过滤器)集成到scrapy-redis中,判断URL是否重复 布隆过滤器(Bloom Filter)详解 基本概念 如 ...

  3. Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?

    使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...

  4. scrapy2_初窥Scrapy

    递归知识:oop,xpath,jsp,items,pipline等专业网络知识,初级水平并不是很scrapy,可以从简单模块自己写. 初窥Scrapy Scrapy是一个为了爬取网站数据,提取结构性数 ...

  5. Python爬虫从入门到放弃(十六)之 Scrapy框架中Item Pipeline用法

    当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...

  6. 三、scrapy后续

    CrawlSpiders 通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl tencent tencent.com 我们通过正则表达 ...

  7. Scrapy爬虫框架第三讲(linux环境)

    下面我们来学习下Spider的具体使用: 我们已上节的百度阅读爬虫为例来进行分析: 1 # -*- coding: utf-8 -*- 2 import scrapy 3 from scrapy.li ...

  8. scrapy分布式爬虫scrapy_redis一篇

    分布式爬虫原理 首先我们来看一下scrapy的单机架构:     可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页 ...

  9. Scrapy爬虫框架(实战篇)【Scrapy框架对接Splash抓取javaScript动态渲染页面】

    (1).前言 动态页面:HTML文档中的部分是由客户端运行JS脚本生成的,即服务器生成部分HTML文档内容,其余的再由客户端生成 静态页面:整个HTML文档是在服务器端生成的,即服务器生成好了,再发送 ...

随机推荐

  1. win10系统无法删除文件的解决方法

    方法/步骤 1:首先进入不能删除的文件所在的文件夹 2:右键单击此文件夹,选择授予访问权限 3:在授权界面选择删除权限 4:在删除权限中点击更改共享权限 5:我们选择administrator级别,点 ...

  2. Apache(httpd)详解

    httpd详解(思维导图) 1. httpd服务 ASF 服务器类型 http服务器 应用程序服务器 httpd的特性 高度模块化 DSO机制 MPM httpd的并发响应模型 prefork wor ...

  3. ESP8266局域网 路由器下作服务器模式串口透传 arduino uno示例 模板参考2

    ESP8266服务器模式串口透传编译需要下载8266的库文件后才可以正常 准备工作 下载一个Arduino IDE,下载8266的库文件ESP8266服务器模式串口透传编译 功能说明 1.直接使用路由 ...

  4. mysql面试题总结

    Mysql中的myisam与innodb的区别? InnoDB存储引擎的四大特性? 什么是事务? 数据库事务的四大特性? 不考虑事务的隔离性,会发生几种问题? MySQL数据库提供的四种隔离级别? 有 ...

  5. linux kernel update

    linux内核升级 最近HW行动,报出来的linux系统内核漏洞,环境中全部是2.6.32-431.el6.x86_64的主机,需要全部升级到754版本,这也是第一次进行内核升级操作. 先找了一台和生 ...

  6. 挖洞入门_显错型SQL注入

    简介:在漏洞盒子挖洞已经有一段时间了,虽说还不是大佬,但技术也有所进步,安全行业就是这样,只有自己动手去做,才能将理论的知识变为个人的经验.本篇文章打算分享一下我在挖显错型SQL注入漏洞过程中的一些个 ...

  7. koa2 的使用方法:(一)

    1. koa2 使用方法: 安装指令是: npm install koa2 使用koa2 创建项目工程: 1. koa2 (项目工程) 2. 进入项目工程: cd 进入您所创建的项目工程 3. npm ...

  8. JAVA相关基础知识

    JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分, ...

  9. SpringBoot读取application.properties中文乱码

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 解决方案 在ID ...

  10. CSV文件导入到数据库中读取数据详解(接着上个帖子)

    一.controller层 二.SERVICE层 @Overridepublic Result importJinjiangAssessResult(MultipartFile file) throw ...