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
相关产品推荐
相关产品推荐

