【问题标题】:Scrapy spider memory leakScrapy spider 内存泄漏
【发布时间】:2015-10-14 03:44:27
【问题描述】:

我的蜘蛛有严重的内存泄漏。运行 15 分钟后,它的内存为 5gb,scrapy 告诉(使用 prefs() )有 900k 个请求对象,仅此而已。如此多的生活请求对象可能是什么原因?请求只会上升,不会下降。所有其他对象都接近于零。

我的蜘蛛是这样的:

class ExternalLinkSpider(CrawlSpider):
  name = 'external_link_spider'
  allowed_domains = ['']
  start_urls = ['']

  rules = (Rule(LxmlLinkExtractor(allow=()), callback='parse_obj', follow=True),)

  def parse_obj(self, response):
    if not isinstance(response, HtmlResponse):
        return
    for link in LxmlLinkExtractor(allow=(), deny=self.allowed_domains).extract_links(response):
        if not link.nofollow:
            yield LinkCrawlItem(domain=link.url)

这里是prefs()的输出

HtmlResponse                        2   oldest: 0s ago 
ExternalLinkSpider                  1   oldest: 3285s ago
LinkCrawlItem                       2   oldest: 0s ago
Request                        1663405   oldest: 3284s ago

在某些网站上,100k 抓取页面的内存可以达到 40gb 标记(例如,在 victorinox.com 上,100k 抓取页面标记达到 35gb 的内存)。在其他方面,它要小得多。

UPD。

【问题讨论】:

    标签: python memory-leaks scrapy scrapyd


    【解决方案1】:

    我马上就发现了一些可能的问题。

    在开始之前,我想提一下 prefs() 不显示排队的请求数,它显示处于活动状态的 Request() 对象的数量。可以引用请求对象并使其保持活动状态,即使它不再排队等待下载。

    我并没有在你提供的代码中看到任何会导致这种情况的东西,但你应该记住这一点。

    马上,我会问:你在使用 cookie 吗?如果没有,将会话 ID 作为 GET 变量传递的站点将为每次页面访问生成一个新的会话 ID。您基本上会一遍又一遍地继续排队相同的页面。例如,victorinox.com 的 URL 字符串中将包含类似“jsessionid=18537CBA2F198E3C1A5C9EE17B6C63AD”的内容,并且每次加载新页面时 ID 都会更改。

    其次,您可能遇到了蜘蛛陷阱。也就是说,一个页面只是重新加载自身,具有无限数量的新链接。想想一个带有“下个月”和“上个月”链接的日历。不过,我没有直接在 victorinox.com 上看到任何内容。

    第三,从提供的代码来看,您的 Spider 不受任何特定域的限制。它将提取在每个页面上找到的每个链接,在每个页面上运行 parse_obj。例如,victorinox.com 的主页有一个指向http://www.youtube.com/victorinoxswissarmy 的链接。这反过来会用大量的 YouTube 链接填充您的请求。

    不过,您需要进行更多故障排除才能准确了解发生了什么。

    您可能想要使用的一些策略:

    1. 创建新的下载器中间件并记录所有请求(到文件或数据库)。审核异常行为请求。
    2. 限制深度以防止它无限地沿着兔子洞向下延伸。
    3. 限制域以测试它是否仍然存在问题。

    如果您发现自己只是合法地生成了许多请求,并且内存是一个问题,请启用持久作业队列并将请求保存到磁盘,而不是。不过,我建议您不要将此作为第一步,因为您的爬虫更有可能无法按照您的意愿工作。

    【讨论】:

    • 第三 - 我的代码仅限于一个域。但域可以由任何。我动态设置允许的域,所以我一次只抓取一个域。至于饼干 - 好点。持久队列-我在scrapy用户组中告诉过他们对于大量请求非常非常慢,所以它不是一个选项:(
    • 好的,你的代码中没有显示,这就是我提到它的原因!不过,持久队列很慢,而且我相信它更多地是为暂停/恢复队列而设计的。在这种情况下,速度差异实际上是内存与磁盘。
    • 我什至设置了一个 FifoMemoryQueue 队列,但最旧的请求对象仍然几乎与蜘蛛对象一样古老。它不应该被处理并发布吗?
    • 你的蜘蛛有多复杂?您是否在任何中间件/处理程序/等中引用并保留请求对象的属性?这样做将使它们绑定到该对象,从而使它们在内存中保持活跃。
    • 基本上你已经展示了我所有的蜘蛛代码,还有 init 方法,我设置了一些动态参数,比如域。至于中间件——除了标准的中间件和设置随机用户代理的中间件和一个随机代理。而且我不在任何地方处理请求,我使用标准的爬虫,如你所见
    猜你喜欢
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-08
    • 2013-01-20
    相关资源
    最近更新 更多