爬虫工作量由小到大的思维转变---<第六十六章 > Scrapy去重机制:BaseDupeFilter与request_fingerprint研究(2)

前言:

继续上一章:爬虫工作量由小到大的思维转变---<第六十五章 > Scrapy去重机制:BaseDupeFilter与request_fingerprint研究-CSDN博客

        在网络爬虫的开发过程中,有效的去重机制是保证爬虫效率和结果准确性的关键。Scrapy作为一个成熟的爬虫框架,通过BaseDupeFilter和request_fingerprint实现了高效的去重功能。但是面对复杂多变的爬取场景,我们还需要深入研究Scrapy的去重机制,并学习如何进行自定义扩展。

本文将详细探讨Scrapy去重机制的实现原理,分析其在电商抓取、新闻聚合等典型案例中的应用,并剖析爬虫中常见的去重难点,以及如何通过扩展BaseDupeFilter、调整request_fingerprint来实现针对特定任务的自定义去重策略。

正文:

1. 自定义去重处理

A. 扩展BaseDupeFilter

自定义去重过滤器的应用场景

虽然Scrapy提供的BaseDupeFilter能够满足大多数情况下的去重需求,但有时候我们需要更复杂的去重逻辑,这就需要自定义去重过滤器。以下是一些需要自定义去重过滤器的常见场景:

(1) 根据页面内容去重

        有些页面即使URL相同,但实际内容可能会不断更新,如果仍然简单根据URL去重会导致丢失新的信息。这时可以解析页面内容,提取诸如更新时间、文章ID等区分内容的字段,根据这些字段判断是否去重。

(2) 分布式爬虫的分布式去重

        在分布式爬虫中,需要确保整个爬虫集群都遵循同样的去重逻辑,避免不同爬虫重复爬取。这需要将去重状态保存到外部数据库或缓存系统中,并让所有爬虫实例共享这个外部状态。

(3) 根据爬取深度去重

        对于爬取非常大的网站,可以根据URL深度来去重,比如深度大于3的URL就不再进行去重,以确保可以爬取到足够多的页面。

(4) 临时性去重

        有时需要根据特定策略临时去重某些请求,而不是永久过滤。这时可以通过自定义的去重逻辑来实现临时过滤请求的效果。

创建与整合自定义过滤器

自定义过滤器需要继承BaseDupeFilter类并实现其方法,主要包括四步:

  • (1) 继承BaseDupeFilter,定义自己的过滤器类
  • (2) 实现父类的方法,编写自定义的去重逻辑
  • (3) 在Spider类中实例化自定义过滤器对象
  • (4) 将自定义过滤器通过spider中间件方法注册到Scrapy引擎

例如:

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

这样就可以通过自定义过滤器实现特定的去重逻辑。

B. 调整request_fingerprint

自定义指纹处理的时机与原因

有时默认的request_fingerprint无法生成区分请求的唯一指纹,这时就需要自定义指纹函数,常见的时机与原因包括:

  • URL包含不稳定的排序参数,需要规范化处理。

  • 需要考虑请求体数据来生成指纹。

  • 分布式爬虫中需要根据服务器实例信息生成指纹。

  • 需要临时忽略某些请求参数。

  • 指纹算法需要提高复杂度来减少碰撞。

等等。

自定义指纹函数的代码实例

自定义指纹函数需要接收Request对象作为参数,并返回一个字符串作为指纹,实现方式有两种:

  • (1) 直接重写request_fingerprint函数
from scrapy.utils.request import request_fingerprint

def my_request_fingerprint(request):
    # 自定义指纹算法
    return xxx 
  • (2) 继承FingerprintGenerator类
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指纹函数,可以实现针对特定爬虫的自定义去重策略。

2. 案例研究与现实问题

A. 多样化爬虫任务中的应用

电子商务产品抓取

电商网站的商品信息页面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)

这样可以根据文章内容差异避免新闻站点中重复内容的抓取。

B. 面临的挑战与解决方案

处理URL参数与会话ID

爬取包含大量变动参数的网站时,这些参数会导致生成不同的指纹从而失效去重。常见情况有:

  • 排序、分页、过滤等参数的变动。

  • 会话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的去重机制,是构建高效稳定爬虫系统的基础。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-02 07:52:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-02 07:52:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-02 07:52:03       87 阅读
  4. Python语言-面向对象

    2024-04-02 07:52:03       96 阅读

热门阅读

  1. 如何反反爬虫

    2024-04-02 07:52:03       34 阅读
  2. Docker环境安装Postgresql数据库Posrgresql 15.6

    2024-04-02 07:52:03       28 阅读
  3. HTTPS工作原理

    2024-04-02 07:52:03       33 阅读
  4. 浅述HTML5的离线存储

    2024-04-02 07:52:03       27 阅读
  5. MongoDB聚合运算符:$lt

    2024-04-02 07:52:03       38 阅读
  6. 云计算概述报告

    2024-04-02 07:52:03       27 阅读
  7. 在课堂中使用 ChatGPT 的 80 个方式(下)

    2024-04-02 07:52:03       34 阅读
  8. 上海大学通信829考研踩坑记录

    2024-04-02 07:52:03       30 阅读