赞
踩
继续上一章:爬虫工作量由小到大的思维转变---<第六十五章 > Scrapy去重机制:BaseDupeFilter与request_fingerprint研究-CSDN博客
在网络爬虫的开发过程中,有效的去重机制是保证爬虫效率和结果准确性的关键。Scrapy作为一个成熟的爬虫框架,通过BaseDupeFilter和request_fingerprint实现了高效的去重功能。但是面对复杂多变的爬取场景,我们还需要深入研究Scrapy的去重机制,并学习如何进行自定义扩展。
本文将详细探讨Scrapy去重机制的实现原理,分析其在电商抓取、新闻聚合等典型案例中的应用,并剖析爬虫中常见的去重难点,以及如何通过扩展BaseDupeFilter、调整request_fingerprint来实现针对特定任务的自定义去重策略。
虽然Scrapy提供的BaseDupeFilter能够满足大多数情况下的去重需求,但有时候我们需要更复杂的去重逻辑,这就需要自定义去重过滤器。以下是一些需要自定义去重过滤器的常见场景:
(1) 根据页面内容去重
有些页面即使URL相同,但实际内容可能会不断更新,如果仍然简单根据URL去重会导致丢失新的信息。这时可以解析页面内容,提取诸如更新时间、文章ID等区分内容的字段,根据这些字段判断是否去重。
(2) 分布式爬虫的分布式去重
在分布式爬虫中,需要确保整个爬虫集群都遵循同样的去重逻辑,避免不同爬虫重复爬取。这需要将去重状态保存到外部数据库或缓存系统中,并让所有爬虫实例共享这个外部状态。
(3) 根据爬取深度去重
对于爬取非常大的网站,可以根据URL深度来去重,比如深度大于3的URL就不再进行去重,以确保可以爬取到足够多的页面。
(4) 临时性去重
有时需要根据特定策略临时去重某些请求,而不是永久过滤。这时可以通过自定义的去重逻辑来实现临时过滤请求的效果。
自定义过滤器需要继承BaseDupeFilter类并实现其方法,主要包括四步:
例如:
- from scrapy.dupefilters import BaseDupeFilter
-
- class CustomDupeFilter(BaseDupeFilter):
-
- # 实现父类方法
- def request_seen(self, request):
- # 自定义去重逻辑
- pass
-
- def open(self):
- # 初始化,如打开数据库连接
- pass
-
- # 其他方法
-
- # Spider类中实例化并注册自定义过滤器
- class MySpider(CrawlSpider):
-
- custom_dupefilter = CustomDupeFilter()
-
- @classmethod
- def from_crawler(cls, crawler):
- # 注册自定义过滤器
- crawler.signals.connect(cls.spider_opened, signal=signals.spider_opened)
- return cls()
-
- def spider_opened(self, spider):
- spider.crawler.engine.scraper.dupefilter = self.custom_dupefilter
这样就可以通过自定义过滤器实现特定的去重逻辑。
有时默认的request_fingerprint无法生成区分请求的唯一指纹,这时就需要自定义指纹函数,常见的时机与原因包括:
URL包含不稳定的排序参数,需要规范化处理。
需要考虑请求体数据来生成指纹。
分布式爬虫中需要根据服务器实例信息生成指纹。
需要临时忽略某些请求参数。
指纹算法需要提高复杂度来减少碰撞。
等等。
自定义指纹函数需要接收Request对象作为参数,并返回一个字符串作为指纹,实现方式有两种:
- from scrapy.utils.request import request_fingerprint
-
- def my_request_fingerprint(request):
- # 自定义指纹算法
- return xxx
- from scrapy.utils.fingerprint import FingerprintGenerator
-
- class MyFingerprintGenerator(FingerprintGenerator):
-
- def __init__(self, crawler):
- super().__init__(settings=crawler.settings)
-
- def fingerprint(self, request):
- # 自定义指纹算法
- return xxx
然后在Settings中配置:
FINGERPRINT_GENERATOR_CLASS = 'mymodule.MyFingerprintGenerator'
这样就可以通过自定义指纹函数,实现更复杂的请求去重逻辑。
Scrapy提供了丰富的扩展点来自定义去重逻辑,以适应各种复杂的爬取场景,比如解析页面内容去重、分布式爬虫的分布式去重等。通过扩展BaseDupeFilter过滤器类以及调整request_fingerprint指纹函数,可以实现针对特定爬虫的自定义去重策略。
电商网站的商品信息页面URL通常包含大量变动参数,比如排序、过滤、分页等参数。这会导致相同商品由于URL参数不同而被重复爬取。
可以采用以下措施优化去重:
在生成指纹前规范化URL,去除排序、过滤等参数。
根据商品ID生成指纹,而不是简单依赖URL。
分析商品详情页面,提取生成日期、价格、库存等区分内容的字段,根据这些字段判断是否需要重新爬取。
根据抓取深度、商品类别等有选择性地进行去重。
代码实现:
- # 自定义request_fingerprint函数
- def product_fingerprint(request):
- # 从url中提取商品id
- product_id = extract_product_id(request.url)
- return hashlib.sha1(product_id.encode('utf8')).hexdigest()
-
- # 自定义过滤器
- class ProductDupeFilter(RFPDupeFilter):
-
- def request_seen(self, request):
- fp = product_fingerprint(request)
- if fp in self.fingerprints:
- return True
- self.fingerprints.add(fp)
- return False
-
- # Spider中使用自定义过滤器
- class ProductSpider(CrawlSpider):
-
- custom_dupefilter = ProductDupeFilter()
-
- # ...
这样可以实现针对电商网站的自定义去重逻辑,避免重复爬取。
新闻站点的文章页面URL中通常包含变动的参数,但实际内容可能重复,直接根据URL去重会导致丢失更新的文章。
可以采用以下措施优化去重:
对URL规范化,去掉变动参数。
分析文章内容,提取发布时间、文章ID等字段,根据这些字段判断是否需要去重。
根据来源网站、主题等有选择性地去重。
代码实现:
- import diff_match_patch as dmp
-
- # 计算页面内容差异
- diff = dmp.diff_main(content1, content2)
- if dmp.diff_levenshtein(diff) < 5:
- # 内容基本一致,可以过滤
-
- # 自定义过滤器
- class NewsDupeFilter(RFPDupeFilter):
-
- def request_seen(self, request):
- # 检查内容差异
- if content_duplicate(request):
- return True
- else:
- return super().request_seen(request)
这样可以根据文章内容差异避免新闻站点中重复内容的抓取。
爬取包含大量变动参数的网站时,这些参数会导致生成不同的指纹从而失效去重。常见情况有:
排序、分页、过滤等参数的变动。
会话ID、访问令牌等用户相关参数。
解决方法:
使用url_query_cleaner规范化URL。
根据实际需求选择性地保留或删除部分参数。
将会话信息抽取出来,不加入指纹。
通过解析页面内容而不是依赖URL去重。
在分布式爬虫中,需要确保所有爬虫节点之间使用统一的去重逻辑,避免重复爬取。
解决方法:
使用外部数据库或缓存系统储存去重状态。
在生成指纹时加入服务器实例信息,确保指纹全局唯一。
使用一致性Hash等算法确定指纹分布。
采用中心节点校验的方式协调去重。
代码实现:
- # 使用Redis存储去重状态
- import redis
-
- r = redis.Redis(host='localhost', port=6379)
-
- class DistributedDupeFilter:
-
- def __init__(self, server_id):
- self.server_id = server_id
-
- def request_seen(self, request):
- fp = generate_fingerprint(request, self.server_id)
- added = r.sadd('fingerprints', fp)
- return not added
-
- # 不同爬虫节点使用不同server_id
- dupefilter = DistributedDupeFilter('crawler1')
这样可以实现分布式爬虫系统的统一去重逻辑。
在多样化的爬虫任务中,需要根据具体场景,选择合适的去重策略。通常结合指纹生成和自定义过滤器,根据页面内容、抓取深度等因素进行差异化的去重可以有效避免重复抓取。而在分布式爬虫中,则需要确保所有节点遵循一致的去重逻辑,采用外部存储可以实现分布式去重状态的共享。Scrapy的高可扩展性为实现自定义去重提供了支持,可以应对各种复杂爬取场景中的去重挑战。
首先介绍了Scrapy去重机制的技术原理,包括BaseDupeFilter的过滤流程、request_fingerprint的指纹生成。进而,结合电商商品抓取、新闻聚合等典型案例,分析了Scrapy去重机制在不同爬虫任务中的具体应用。
然后,剖析了爬虫爬取中常见的去重难点,如处理复杂变动参数、实现分布式爬虫的统一去重等,并给出了相应的解决方案。
最后,通过自定义扩展BaseDupeFilter过滤器类以及调整request_fingerprint指纹函数两个方面,详细讲解了如何实现针对特定爬取场景的个性化去重策略。
--------这为我们应对各种复杂爬虫任务中的去重挑战提供了重要参考。总之,充分理解和灵活应用Scrapy的去重机制,是构建高效稳定爬虫系统的基础。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。