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

如何在Scrapy爬虫结束前重试未完成的请求?

Scrapy爬虫结束前重试未完成URL的解决方案

问题描述

使用Scrapy和Python3.6.8搭建爬虫时,尝试通过__del__方法在爬虫类销毁前重试记录的未完成URL,但仅执行打印操作后爬虫直接退出,重试逻辑未生效。相关代码如下:

记录未完成URL的逻辑:

self.urls.append(url) 

item = myItem()
item["mylink"] = url        
yield scrapy.Request(            
       url="myurl",
       method='GET',
       headers=self.headers,
       callback=self.parse_detail,
       errback=self.make_new_request,
       meta={"item":item})

尝试在析构函数中重试:

def __del__(self):
    print("\033[31myielded:",len(self.yielded),"scrapying:",len(self.urls),"\033[0m")
    if len(self.urls)>0:
        print(self.urls)
        print("\033[31mretry uncompleted\033[0m")
        self.RetryUncompletedUrls()

原因分析

__del__是Python的析构函数,当它被调用时,Scrapy的爬虫生命周期已接近尾声,引擎已经停止调度新请求。此时即使调用重试方法,也无法将请求提交给引擎执行,因此只会打印日志后直接退出。

正确实现方案

使用Scrapy的**spider_closed信号**,该信号在爬虫准备关闭时触发,此时引擎仍可接受并调度新请求,适合处理未完成URL的重试。

完整代码示例

from scrapy import signals
from scrapy.exceptions import DontCloseSpider
import scrapy

class myItem(scrapy.Item):
    mylink = scrapy.Field()

class MySpider(scrapy.Spider):
    name = "my_spider"
    urls = []  # 存储未完成/失败的URL
    yielded = []  # 记录已提交的请求数

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 连接spider_closed信号到自定义处理函数
        self.crawler.signals.connect(self.retry_uncompleted_urls, signal=signals.spider_closed)

    def start_requests(self):
        # 初始请求逻辑(示例)
        yield scrapy.Request(url="初始URL", callback=self.parse)

    def parse(self, response):
        # 解析页面生成待爬取URL(示例逻辑)
        url = "待爬取的目标URL"
        self.urls.append(url)
        item = myItem()
        item["mylink"] = url
        self.yielded.append(url)
        yield scrapy.Request(
            url=url,
            method='GET',
            headers=self.headers,
            callback=self.parse_detail,
            errback=self.handle_request_failure,
            meta={"item": item, "retry_count": 0}  # 添加重试计数,避免无限重试
        )

    def parse_detail(self, response):
        # 详情页解析逻辑
        item = response.meta["item"]
        # 解析完成后从urls中移除该URL
        if item["mylink"] in self.urls:
            self.urls.remove(item["mylink"])
        # 处理并输出item
        yield item

    def handle_request_failure(self, failure):
        # 请求失败时的处理逻辑
        request = failure.request
        item = request.meta["item"]
        retry_count = request.meta.get("retry_count", 0)
        
        # 设置最大重试次数(示例为3次)
        max_retry = 3
        if retry_count < max_retry:
            # 更新重试计数,重新发起请求
            new_meta = request.meta.copy()
            new_meta["retry_count"] = retry_count + 1
            self.logger.warning(f"请求失败,重试第{new_meta['retry_count']}次: {item['mylink']}")
            yield request.replace(meta=new_meta)
        else:
            # 超过重试次数,加入未完成URL列表等待最终重试
            if item["mylink"] not in self.urls:
                self.urls.append(item["mylink"])

    def retry_uncompleted_urls(self, spider, reason):
        # 处理未完成URL的最终重试
        if not self.urls:
            return
        
        self.logger.info(f"开始重试{len(self.urls)}个未完成/失败的URL")
        for url in self.urls.copy():  # 使用copy避免遍历中修改列表
            item = myItem()
            item["mylink"] = url
            # 构造重试请求,设置初始重试计数
            req = scrapy.Request(
                url=url,
                method='GET',
                headers=self.headers,
                callback=self.parse_detail,
                errback=self.handle_request_failure,
                meta={"item": item, "retry_count": 0}
            )
            # 直接通过引擎调度请求
            self.crawler.engine.crawl(req, spider=self)
            # 从列表中移除,避免重复处理
            self.urls.remove(url)
        
        # 抛出DontCloseSpider,阻止爬虫立即关闭,直到重试请求处理完成
        raise DontCloseSpider("正在处理未完成URL的重试请求")

关键说明

  1. 信号连接:在爬虫初始化时连接spider_closed信号到处理函数,确保在爬虫关闭前触发重试逻辑。
  2. 请求调度:通过self.crawler.engine.crawl()直接将重试请求提交给Scrapy引擎,确保请求被执行。
  3. 避免无限重试:给每个请求添加retry_count元数据,设置最大重试次数,防止陷入死循环。
  4. 阻止爬虫关闭:抛出DontCloseSpider异常,让爬虫继续运行直到所有重试请求处理完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 06:35:29