You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Scrapy爬虫的可复现性且不牺牲性能?

问题

需求:让Scrapy爬虫具备可复现性,即对同一静态网站两次爬取时,页面访问顺序完全一致,parse函数打印的URL每次均相同。

已尝试实现:

  1. 定义SiteSpider类:
class SiteSpider(CrawlSpider):
    name = 'ReproducibleSpider'
    rules = [Rule(LinkExtractor(), callback='parse', follow=True)]

    def __init__(self, urls: list[str], domains: list[str]):
        super().__init__()
        self.start_urls = urls
        self.allowed_domain = domains

    def parse(self, response, **kwargs):
        url = response.url if hasattr(response, 'url') else None
        print(url)
        return None
  1. 自定义SortedLinkExtractor类对提取的链接排序:
class SortedLinkExtractor(LinkExtractor):
    def extract_links(self, response):
        links = super().extract_links(response)
        return sorted(links, key=lambda x: len(x.url))

核心问题:Scrapy的并发请求机制导致爬取顺序不可复现。通过设置CONCURRENT_REQUESTS = 1和CONCURRENT_REQUESTS_PER_DOMAIN = 1虽能实现可复现,但完全牺牲了爬虫性能,需要兼顾性能的解决方案。

解决方案

要同时保证并发性能和爬取顺序的可复现性,核心是让Scrapy的请求队列按固定规则排序,同时保留并发执行能力,具体实现步骤如下:

1. 给请求分配固定优先级

Scrapy调度器默认随机处理请求,需为每个请求分配唯一且固定的优先级,确保相同URL的请求优先级一致,队列顺序固定。可在parse方法中手动生成Request并设置优先级:

def parse(self, response, **kwargs):
    url = response.url if hasattr(response, 'url') else None
    print(url)
    
    # 使用自定义SortedLinkExtractor提取链接
    link_extractor = SortedLinkExtractor()
    links = link_extractor.extract_links(response)
    
    # 基于URL哈希值设置固定优先级,相同URL优先级完全一致
    for link in links:
        yield scrapy.Request(
            url=link.url,
            callback=self.parse,
            priority=abs(hash(link.url))
        )

注意:需替换原Rule方式,改用手动生成Request以完全控制请求优先级。

2. 配置调度器使用有序队列

修改Scrapy配置,确保调度器严格按优先级执行请求,同时关闭随机化设置:

# settings.py
# 使用默认调度器,确保优先级队列生效
SCHEDULER = 'scrapy.core.scheduler.Scheduler'
# 内存队列使用优先级队列
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.PriorityMemoryQueue'
# 关闭下载延迟随机化,避免顺序波动
RANDOMIZE_DOWNLOAD_DELAY = False

如需持久化队列(如断点续爬),可设置磁盘队列为有序类型:

SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'

3. 优化SortedLinkExtractor的排序规则

将链接排序规则调整为更稳定的URL字典序,避免因URL长度相同导致的顺序不确定:

class SortedLinkExtractor(LinkExtractor):
    def extract_links(self, response):
        links = super().extract_links(response)
        # 按URL字典序排序,保证提取顺序完全固定
        return sorted(links, key=lambda x: x.url)

4. 固定爬虫环境配置

两次爬取需保持完全一致的配置:

  • 固定USER_AGENT为同一值
  • 设置固定DOWNLOAD_DELAY(不启用随机化)
  • 禁用所有引入随机性的中间件(如随机User-Agent中间件)

通过以上配置,既能保留高并发设置(如CONCURRENT_REQUESTS = 8)保证性能,又能确保两次爬取的页面访问顺序和parse打印的URL顺序完全一致。


内容的提问来源于stack exchange,提问作者Amir H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:01:37