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

Scrapy爬虫启动后运行满20秒暂停请求再恢复的实现问题

问题描述

需要实现Scrapy爬取控制逻辑:爬虫从启动开始累计运行时长达到20秒时,暂停所有请求发送指定秒数,之后恢复正常爬取流程直到全部任务完成。现有脚本未实现预期暂停效果,爬虫始终保持正常运行,需要修改代码实现功能。

原有代码问题点
  • 时间判断逻辑完全失效:判断条件self.datetime == (self.datetime + datetime.timedelta(seconds = 20)).time()永远无法成立,左侧是启动时记录的datetime对象,右侧是time类型值,类型不匹配,且逻辑上拿固定值和自身叠加后的值对比,不可能满足相等条件
  • 暂停逻辑无实际作用:代码中创建的是局部Deferred对象,没有和Scrapy的请求调度流程绑定,且调用pause()后立刻调用unpause(),没有任何等待时长,完全不会产生暂停效果,另外Deferred.pause()也不接收请求计数作为参数
  • 信号选择错误:绑定request_scheduled信号做时长统计没有意义,该信号仅在请求被加入调度队列时触发,无法准确统计爬虫实际运行时长,也不能拦截请求发送动作
实现方案

要实现全局请求暂停,最可靠的方式是通过下载器中间件拦截所有待发送请求,配合Twisted的延迟工具实现定时暂停,具体修改步骤:

  • 移除原有无效的信号绑定、时间判断、暂停相关代码
  • 自定义下载中间件,在中间件中记录爬虫启动时间、暂停触发标记、需要暂停的时长
  • 每次请求经过中间件时,校验运行时长,达到20秒且未触发过暂停时,返回延迟指定秒数的Deferred,延迟结束后自动恢复请求放行
  • 在自定义配置中注册编写好的下载中间件

修改后的完整可运行代码:

import scrapy
from scrapy import signals
import datetime
from twisted.internet import defer, reactor

# 自定义暂停控制中间件
class PauseSpiderMiddleware:
    def __init__(self, pause_after_seconds, pause_duration):
        self.start_time = None
        self.pause_triggered = False
        self.pause_after = pause_after_seconds
        self.pause_duration = pause_duration

    @classmethod
    def from_crawler(cls, crawler):
        ext = cls(
            pause_after_seconds=crawler.settings.getint('PAUSE_AFTER_SECONDS', 20),
            pause_duration=crawler.settings.getint('PAUSE_DURATION', 10)
        )
        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        return ext

    def spider_opened(self, spider):
        self.start_time = datetime.datetime.now()

    def process_request(self, request, spider):
        if self.pause_triggered:
            return None
        run_seconds = (datetime.datetime.now() - self.start_time).total_seconds()
        if run_seconds >= self.pause_after:
            self.pause_triggered = True
            spider.logger.info(f"已运行{run_seconds:.1f}秒,暂停爬取{self.pause_duration}秒")
            d = defer.Deferred()
            reactor.callLater(self.pause_duration, d.callback, None)
            return d
        return None

class TestSpider(scrapy.Spider):
    name = 'signal'

    start_urls = [f'https://www.meadowhall.co.uk/eatdrinkshop?page={i}' for i in range(1, 15)]

    custom_settings = {
        'DOWNLOAD_DELAY':2,
        'DOWNLOADER_MIDDLEWARES': {
            '__main__.PauseSpiderMiddleware': 1,
        },
        'PAUSE_AFTER_SECONDS': 20,
        'PAUSE_DURATION': 10
    }

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                cb_kwargs = {
                    'pg':url
                }
            ) 
    def parse(self, response,pg):
        content_page = response.xpath("//div[@class='view-content']//div")
        url_split = pg.split('?')[-1]
        for cnt in content_page:
            image_url = cnt.xpath(".//img//@src").get()
            if image_url != None:
                yield {
                    'image_urls':image_url,
                    'url_page':url_split
                }
代码说明
  • 暂停逻辑仅触发一次,触发后不再拦截请求,不影响后续爬取流程
  • 可直接修改PAUSE_DURATION配置项调整暂停时长,无需改动核心逻辑
  • 中间件优先级设置为1,优先于其他下载中间件拦截请求,保证所有待发请求都受暂停逻辑控制
  • 暂停动作不会中断已经在处理中的请求,仅阻塞暂停触发后待发送的新请求,符合爬取控制要求

内容的提问来源于stack exchange,提问作者Working dollar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:18:15