当前位置: 首页 > news >正文

高级深入--day46

dupefilter.py

负责执行requst的去重,实现的很有技巧性,使用redis的set数据结构。但是注意scheduler并不使用其中用于在这个模块中实现的dupefilter键做request的调度,而是使用queue.py模块中实现的queue。

当request不重复时,将其存入到queue中,调度时将其弹出。

import logging
import timefrom scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import request_fingerprintfrom .connection import get_redis_from_settingsDEFAULT_DUPEFILTER_KEY = "dupefilter:%(timestamp)s"logger = logging.getLogger(__name__)# TODO: Rename class to RedisDupeFilter.
class RFPDupeFilter(BaseDupeFilter):"""Redis-based request duplicates filter.This class can also be used with default Scrapy's scheduler."""logger = loggerdef __init__(self, server, key, debug=False):"""Initialize the duplicates filter.Parameters----------server : redis.StrictRedisThe redis server instance.key : strRedis key Where to store fingerprints.debug : bool, optionalWhether to log filtered requests."""self.server = serverself.key = keyself.debug = debugself.logdupes = True@classmethoddef from_settings(cls, settings):"""Returns an instance from given settings.This uses by default the key ``dupefilter:<timestamp>``. When using the``scrapy_redis.scheduler.Scheduler`` class, this method is not used asit needs to pass the spider name in the key.Parameters----------settings : scrapy.settings.SettingsReturns-------RFPDupeFilterA RFPDupeFilter instance."""server = get_redis_from_settings(settings)# XXX: This creates one-time key. needed to support to use this# class as standalone dupefilter with scrapy's default scheduler# if scrapy passes spider on open() method this wouldn't be needed# TODO: Use SCRAPY_JOB env as default and fallback to timestamp.key = DEFAULT_DUPEFILTER_KEY % {'timestamp': int(time.time())}debug = settings.getbool('DUPEFILTER_DEBUG')return cls(server, key=key, debug=debug)@classmethoddef from_crawler(cls, crawler):"""Returns instance from crawler.Parameters----------crawler : scrapy.crawler.CrawlerReturns-------RFPDupeFilterInstance of RFPDupeFilter."""return cls.from_settings(crawler.settings)def request_seen(self, request):"""Returns True if request was already seen.Parameters----------request : scrapy.http.RequestReturns-------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 == 0def request_fingerprint(self, request):"""Returns a fingerprint for a given request.Parameters----------request : scrapy.http.RequestReturns-------str"""return request_fingerprint(request)def close(self, reason=''):"""Delete data on close. Called by Scrapy's scheduler.Parameters----------reason : str, optional"""self.clear()def clear(self):"""Clears fingerprints data."""self.server.delete(self.key)def log(self, request, spider):"""Logs given request.Parameters----------request : scrapy.http.Requestspider : scrapy.spiders.Spider"""if self.debug:msg = "Filtered duplicate request: %(request)s"self.logger.debug(msg, {'request': request}, 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)")msg = "Filtered duplicate request: %(request)s"self.logger.debug(msg, {'request': request}, extra={'spider': spider})self.logdupes = False

这个文件看起来比较复杂,重写了scrapy本身已经实现的request判重功能。因为本身scrapy单机跑的话,只需要读取内存中的request队列或者持久化的request队列(scrapy默认的持久化似乎是json格式的文件,不是数据库)就能判断这次要发出的request url是否已经请求过或者正在调度(本地读就行了)。而分布式跑的话,就需要各个主机上的scheduler都连接同一个数据库的同一个request池来判断这次的请求是否是重复的了。

在这个文件中,通过继承BaseDupeFilter重写他的方法,实现了基于redis的判重。根据源代码来看,scrapy-redis使用了scrapy本身的一个fingerprint接request_fingerprint,这个接口很有趣,根据scrapy文档所说,他通过hash来判断两个url是否相同(相同的url会生成相同的hash结果),但是当两个url的地址相同,get型参数相同但是顺序不同时,也会生成相同的hash结果(这个真的比较神奇。。。)所以scrapy-redis依旧使用url的fingerprint来判断request请求是否已经出现过。

这个类通过连接redis,使用一个key来向redis的一个set中插入fingerprint(这个key对于同一种spider是相同的,redis是一个key-value的数据库,如果key是相同的,访问到的值就是相同的,这里使用spider名字+DupeFilter的key就是为了在不同主机上的不同爬虫实例,只要属于同一种spider,就会访问到同一个set,而这个set就是他们的url判重池),如果返回值为0,说明该set中该fingerprint已经存在(因为集合是没有重复值的),则返回False,如果返回值为1,说明添加了一个fingerprint到set中,则说明这个request没有重复,于是返回True,还顺便把新fingerprint加入到数据库中了。 DupeFilter判重会在scheduler类中用到,每一个request在进入调度之前都要进行判重,如果重复就不需要参加调度,直接舍弃就好了,不然就是白白浪费资源。

相关文章:

  • 第一章 03Java入门-编写第一个Java程序HelloWorld以及JVM、JDK和JRE概念
  • promise最初理解
  • 系统提示缺少或找不到emp.dll文件的详细解决方案
  • 力扣每日一题100:相同的树
  • 【软件工程】金管局计算机岗位——软件测试的分类(⭐⭐⭐⭐)
  • 在Spring Boot中使用国产数据库连接池Druid
  • webpack 与 grunt、gulp 的不同?
  • C++虚表与虚表指针详解
  • 【位运算】XOR Construction—CF1895D
  • 初识Vue 输出Hello World 及注意事项
  • [GDOUCTF 2023]<ez_ze> SSTI 过滤数字 大括号{等
  • 【漏洞复现】Apache_HTTPD_多后缀解析漏洞
  • 奇元大模型通过备案 360自研两大模型均获批
  • Python之Excel数据相关
  • 【自动化测试教程】Java+Selenium自动化测试环境搭建
  • SegmentFault for Android 3.0 发布
  • Android单元测试 - 几个重要问题
  • angular组件开发
  • css系列之关于字体的事
  • es6(二):字符串的扩展
  • input实现文字超出省略号功能
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 代理模式
  • 高性能JavaScript阅读简记(三)
  • 记录一下第一次使用npm
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 浏览器缓存机制分析
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 学习笔记:对象,原型和继承(1)
  • 译自由幺半群
  • 中文输入法与React文本输入框的问题与解决方案
  • Spring Batch JSON 支持
  • 从如何停掉 Promise 链说起
  • 正则表达式-基础知识Review
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • $$$$GB2312-80区位编码表$$$$
  • $.ajax中的eval及dataType
  • (1)常见O(n^2)排序算法解析
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (31)对象的克隆
  • (python)数据结构---字典
  • (附源码)ssm高校实验室 毕业设计 800008
  • (转)项目管理杂谈-我所期望的新人
  • .NET 设计模式初探
  • .NET/C# 的字符串暂存池
  • .NET分布式缓存Memcached从入门到实战
  • ??javascript里的变量问题
  • @Autowired @Resource @Qualifier的区别
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • @基于大模型的旅游路线推荐方案
  • @开发者,一文搞懂什么是 C# 计时器!