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

如何实现Scrapy运行指定时长后暂停,按下任意键恢复?

Scrapy定时暂停并等待按键恢复的实现方案

问题分析

你当前的代码存在两个核心问题:

  1. 定时器线程中调用input()只会阻塞该线程,无法暂停Scrapy主线程的爬虫逻辑——Scrapy引擎运行在独立主线程中,线程间的阻塞互不影响。
  2. 未使用Scrapy官方提供的引擎控制接口实现暂停/恢复,而是试图通过线程阻塞实现,不符合Scrapy的工作机制。

解决方案

利用Scrapy原生的crawler.engine.pause()和crawler.engine.resume()方法控制爬虫状态,结合独立线程处理按键输入,避免阻塞主线程。以下是完整实现代码:

完整中间件代码

from scrapy import signals
from threading import Timer, Thread
import subprocess
import sys
import termios
import tty

class RepeatTimer(Timer):
    def run(self):
        while not self.finished.wait(self.interval):
            self.function(*self.args, **self.kwargs)

CMD = '''
on run argv
  display notification (item 2 of argv) with title (item 1 of argv)
end run
'''

class PauseOnTimerMiddleware:
    def __init__(self):
        self.timer = None
        self.crawler = None

    def notify(self, title, text):
        # 发送系统通知
        subprocess.call(['osascript', '-e', CMD, title, text])
        # 暂停Scrapy引擎
        self.crawler.engine.pause()
        print("\nScrapy已暂停,按下任意键恢复运行...")
        # 启动独立线程等待按键,避免阻塞主线程
        Thread(target=self.wait_for_key).start()

    def wait_for_key(self):
        # 保存终端原有配置
        old_settings = termios.tcgetattr(sys.stdin)
        try:
            # 设置终端为原始模式,捕获任意按键(无需回车)
            tty.setcbreak(sys.stdin.fileno())
            # 读取单个按键
            sys.stdin.read(1)
            # 恢复Scrapy引擎
            self.crawler.engine.resume()
            print("\nScrapy已恢复运行")
        finally:
            # 恢复终端原有配置,避免终端异常
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        s.crawler = crawler
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)
        return s

    def spider_opened(self, spider):
        # 启动定时任务,此处设置为40秒触发一次
        self.timer = RepeatTimer(40, self.notify, args=("GetBooks Timer", "Change Your IP Address It Has Been 40 Seconds GETSBOOK"))
        self.timer.start()
        spider.logger.info("定时暂停器已启动")

    def spider_closed(self, spider):
        # 爬虫关闭时终止定时器,避免资源泄漏
        if self.timer:
            self.timer.cancel()
        spider.logger.info("定时暂停器已停止")

代码说明

  1. 定时器逻辑:保留原有RepeatTimer实现,实现周期性触发暂停逻辑。
  2. 引擎控制:通过crawler.engine.pause()/resume()实现官方标准的爬虫暂停/恢复,确保状态安全。
  3. 按键处理:将按键等待逻辑放入独立线程,避免阻塞Scrapy主线程;通过终端原始模式实现"任意按键"触发,无需回车确认。
  4. 生命周期绑定:通过spider_opened/spider_closed信号绑定定时器的启动与销毁,保证资源合理释放。

启用中间件

在项目的settings.py中配置启用该中间件:

DOWNLOADER_MIDDLEWARES = {
    '你的项目名.middlewares.PauseOnTimerMiddleware': 543,
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:09:47