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

Scrapy结合CrawlerProcess与TwistedScheduler运行后异常终止问题排查

问题排查方向

1. Twisted Scheduler 异常未捕获或资源泄漏

  • 检查调度任务的回调函数,确保所有代码都被try-except包裹,尤其是涉及网络请求、数据处理的部分——未捕获的异常会直接终止Twisted Reactor,且不会触发Scrapy的日志输出。
  • 用top或ps aux监控爬虫进程的内存占用,如果内存持续攀升直到被系统OOM Killer杀掉,进程会直接终止无日志。这种情况要排查是否有内存泄漏,比如爬虫里的全局变量累积了大量数据未释放。

2. Scrapy 中间件隐性错误

  • 即使代理测试正常,也要检查代理中间件的重试、切换逻辑:比如代理连接超时后是否有未处理的异常,或者重试次数过多导致的死循环。可以在中间件的process_request/process_response里添加详细日志,记录每个代理的使用状态。
  • 排查所有自定义中间件(下载、爬虫、Spider中间件),确保没有抛出未捕获的异常——Scrapy的中间件如果出现未处理的错误,可能直接导致Reactor停止。

3. 系统层面限制

  • 检查文件描述符限制:爬虫运行时会打开大量socket连接,超过系统默认的文件描述符上限(一般是1024)会导致进程意外退出。用ulimit -n查看当前限制,临时调高到10240测试。
  • 查看系统OOM日志(比如dmesg | grep -i oom),如果存在Out of memory: Killed process的记录,说明是系统因内存不足杀掉了爬虫进程。

4. Twisted Reactor 全局异常捕获

给Reactor添加全局异常监听器,捕获所有未被Scrapy处理的底层错误:

from twisted.python import log
import sys

def log_unhandled_errors(event):
    if 'failure' in event:
        print("UNHANDLED TWISTED FAILURE:", file=sys.stderr)
        event['failure'].printTraceback(file=sys.stderr)

log.startLoggingWithObserver(log_unhandled_errors, setStdout=False)

把这段代码放在爬虫启动前(比如CrawlerProcess初始化之前),能捕获Twisted层面的隐性错误。

日志优化建议
  • 调高Scrapy日志级别并持久化:
    在settings.py中配置:

    LOG_LEVEL = 'DEBUG'
    LOG_FILE = 'spider_full.log'
    LOG_STDOUT = True  # 把print输出也写入日志
    LOG_FORMAT = '%(asctime)s [%(name)s] %(levelname)s: %(message)s'
    

    这样所有调试信息(包括请求头、响应状态、中间件处理步骤)都会被记录,方便回溯终止前的状态。

  • 添加资源监控日志:
    自定义一个监控中间件,定期输出内存、连接数等关键指标:

    import psutil
    from scrapy import signals
    from scrapy.exceptions import NotConfigured
    
    class ResourceMonitorMiddleware:
        def __init__(self, interval=300):
            self.interval = interval
            self.last_log = 0
    
        @classmethod
        def from_crawler(cls, crawler):
            if not crawler.settings.getbool('RESOURCE_MONITOR_ENABLED'):
                raise NotConfigured
            interval = crawler.settings.getint('RESOURCE_MONITOR_INTERVAL', 300)
            s = cls(interval)
            crawler.signals.connect(s.process_request, signal=signals.request_received)
            return s
    
        def process_request(self, request, spider):
            current_time = int(request.meta.get('download_slot', 0))
            if current_time - self.last_log >= self.interval:
                process = psutil.Process()
                spider.logger.info(f"Memory usage: {process.memory_info().rss / 1024 / 1024:.2f} MB")
                spider.logger.info(f"Open connections: {len(process.connections())}")
                spider.logger.info(f"Open files: {len(process.open_files())}")
                self.last_log = current_time
    

    在settings.py中启用:

    DOWNLOADER_MIDDLEWARES = {
        'your_project.middlewares.ResourceMonitorMiddleware': 543,
    }
    RESOURCE_MONITOR_ENABLED = True
    RESOURCE_MONITOR_INTERVAL = 300  # 每5分钟输出一次
    
  • 启用Scrapy内置统计日志:
    在settings.py中开启LogStats扩展,定期输出爬虫运行统计:

    EXTENSIONS = {
        'scrapy.extensions.logstats.LogStats': 100,
    }
    LOGSTATS_INTERVAL = 60  # 每分钟输出一次请求、响应、错误数量
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 18:52:36