You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动