如何在Scrapy爬虫触发超时或键盘中断后停止抓取已入队的请求
如何在Scrapy爬虫触发超时或键盘中断后停止抓取已入队的请求
这个问题我之前也遇到过!Scrapy默认确实会坚持处理完队列里所有已入队的请求才会进入关闭流程,哪怕你触发了超时或者键盘中断,这就导致明明设了1分钟超时却拖了很久才结束。不过有几个直接有效的方法可以解决:
方法一:自定义超时定时器,到点直接关闭爬虫
你可以在爬虫初始化的时候设置一个Twisted定时器,到达指定超时时间后直接调用Scrapy引擎的close_spider()方法,这个方法会立刻停止调度新的待处理请求,直接进入爬虫关闭流程,不会再去处理队列里的pending请求。
代码示例:
from twisted.internet import reactor import time import scrapy class MyTargetSpider(scrapy.Spider): name = "target_spider" start_urls = ["https://example.com"] # 自定义超时时间,单位秒 CUSTOM_TIMEOUT = 60 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.start_timestamp = time.time() # 启动超时定时器 self.timeout_trigger = reactor.callLater(self.CUSTOM_TIMEOUT, self.handle_timeout) def handle_timeout(self): # 触发爬虫关闭,直接终止待处理请求队列 self.crawler.engine.close_spider(self, reason="Timeout reached, stopping immediately") def parse(self, response): # 可选:在生成新请求前检查是否已经超时,避免继续添加新请求到队列 if time.time() - self.start_timestamp > self.CUSTOM_TIMEOUT: self.logger.info("Timeout exceeded, stopping request generation") return # 你的页面解析逻辑,生成新请求的代码 yield scrapy.Request( url="https://example.com/next-page", callback=self.parse, dont_filter=True )
方法二:监听键盘中断信号,触发立即关闭
对于键盘中断(Ctrl+C)的场景,你可以通过Scrapy的爬虫初始化钩子监听系统的SIGINT信号,收到中断信号时直接调用close_spider(),跳过队列处理流程。
代码示例:
import signal import scrapy from scrapy import signals class MyTargetSpider(scrapy.Spider): name = "target_spider" start_urls = ["https://example.com"] @classmethod def from_crawler(cls, crawler, *args, **kwargs): spider = super().from_crawler(crawler, *args, **kwargs) # 监听SIGINT信号(也就是Ctrl+C) def handle_keyboard_interrupt(sig, frame): crawler.engine.close_spider(spider, reason="Keyboard interrupt received, stopping immediately") signal.signal(signal.SIGINT, handle_keyboard_interrupt) return spider def parse(self, response): # 你的页面解析逻辑 yield scrapy.Request( url="https://example.com/next-page", callback=self.parse )
注意事项
close_spider()会立刻停止调度新的待处理请求,但已经处于下载中的请求可能会继续完成(这是Twisted网络框架的特性,没办法强制终止正在进行的下载),不过这已经能大大缩短从触发停止到进入close()方法的时间。- 确保你在爬虫的
closed()方法里的数据保存逻辑是同步执行的,或者用Twisted的Deferred对象处理异步操作,避免在关闭过程中丢失数据。
内容来源于stack exchange




