Python中Scrapy框架的active_size大小持续增长问题如何解决?

我的理解 engine.scraper.slot.active_size 是 engine.scraper.slot.active 中的抓取器处理的响应大小。
但是用 telnet 查看爬虫时发现 engine.scraper.slot.active 一直是 0,但是 engine.scraper.slot.active_size 的值确越来越大。
有没有哪位朋友碰到过这种情况呢?想咨询下造成这种问题的原因。
谢谢
Python中Scrapy框架的active_size大小持续增长问题如何解决?

2 回复

在Scrapy里,active_size 持续增长通常是因为请求或响应没被正确回收,内存泄漏了。核心是检查你的 SpiderMiddleware 里有没有循环引用或者没及时关闭的资源。

最常见的原因是在 Spider 的回调方法(比如 parse)里,把 Response 或它内部的 Selector 对象存到了实例变量(self.xxx)或者某个全局缓存里,导致整个响应链没法被垃圾回收。

给你个排查方向和代码示例:

  1. 首先,检查你的 Spider:确保没有把 responseresponse.css/response.xpath 得到的 SelectorList 长期保存。

    # 错误示例:将SelectorList存到实例变量
    class MySpider(scrapy.Spider):
        name = 'leaky_spider'
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.cached_selectors = [] # 这个列表会持续增长!
    
        def parse(self, response):
            # 错误:将选择器对象存入实例的列表,导致其无法释放
            self.cached_selectors.append(response.css('div.title'))
            # ... 其他逻辑
    

    正确做法:只在回调方法内部临时使用这些对象,提取出需要的数据(字符串、数字等)。

    # 正确示例:只提取数据
    class MySpider(scrapy.Spider):
        name = 'clean_spider'
        def parse(self, response):
            # 立即提取出文本数据,而不是保存Selector对象
            titles = response.css('div.title::text').getall()
            for title in titles:
                item = {'title': title}
                # ... 可能处理或yield item
            # response及其内部的选择器在此方法结束后即可被安全回收
    
  2. 检查自定义的 Downloader Middleware 或 Spider Middleware:如果你在 process_responseprocess_request 方法中修改了 requestresponse 对象,并附加了大的数据对象,也可能导致问题。确保这些附加数据是必要的,并且其生命周期被妥善管理。

  3. 使用内置工具定位:用 telnetscrapy.utils.trackref 来追踪活跃的引用。运行爬虫时,通过 telnet localhost 6023 连接,使用 prefs() 命令可以查看活跃的对象数量,帮助判断是哪类对象在增长。

总结建议:重点检查Spider中是否长期持有了Response或Selector对象。


做个结帖。
这个问题的原因是:出于某种原因,我们在处理 response 时,替换了 body,导致最后 scraper 减去的 response 的 size 于了下载器下载的大小。当出现较多处理时,会当值 active_size 超过了 5M 的阈值。会造成爬虫一直卡住,不处理下一个请求。
这个问题一般也不会出现,大家可以忽略。

回到顶部