Scrapy中的记忆泄漏

时间:2015-05-25 15:03:55

标签: python web-scraping scrapy

我编写了以下代码来搜索电子邮件地址(用于测试目的):

import scrapy
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.selector import Selector
from crawler.items import EmailItem

class LinkExtractorSpider(CrawlSpider):
    name = 'emailextractor'
    start_urls = ['http://news.google.com']

    rules = ( Rule (LinkExtractor(), callback='process_item', follow=True),)

    def process_item(self, response):
        refer = response.url
        items = list()
        for email in Selector(response).re("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}"):

            emailitem = EmailItem()
            emailitem['email'] = email
            emailitem['refer'] = refer
            items.append(emailitem)
        return items

不幸的是,似乎没有正确关闭对请求的引用,因为scrapy telnet控制台的请求数量增加了5k / s。在约3分钟和10k刮页后,我的系统开始交换(8GB RAM)。 任何人都知道出了什么问题? 我已经尝试删除引用并使用

“复制”字符串
emailitem['email'] = ''.join(email)

没有成功。 在抓取之后,这些项目会被保存到BerkeleyDB中,计算它们的出现次数(使用管道),因此引用应该在此之后消失。

退回一组物品和分别产生每件物品有什么区别?

编辑:

经过一段时间的调试后我发现,请求没有被释放,所以我最终得到:

$> nc localhost 6023
>>> prefs()
Live References
Request 10344   oldest: 536s ago
>>> from scrapy.utils.trackref import get_oldest
>>> r = get_oldest('Request')
>>> r.url
<GET http://news.google.com>

实际上是起始网址。 谁知道问题是什么?缺少对Request对象的引用在哪里?

EDIT2:

在服务器(具有64GB RAM)上运行约12小时后,使用的RAM大约为16GB(使用ps,即使ps不是正确的工具)。问题是,抓取的页面数量大幅下降,并且从小时开始,抓取的项目数量仍为0:

INFO: Crawled 122902 pages (at 82 pages/min), scraped 3354 items (at 0 items/min)

EDIT3: 我做了objgraph分析,结果如下图(感谢@Artur Gaspar): Python Objgraph Backlink

似乎我无法影响它?

3 个答案:

答案 0 :(得分:7)

对我来说,最后一个答案是将基于磁盘的队列与工作目录结合使用作为运行时参数。

这是将以下代码添加到settings.py:

DEPTH_PRIORITY = 1 
SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'

之后,使用以下命令行启动爬网程序会使更改在给定目录中保持不变:

scrapy crawl {spidername} -s JOBDIR = crawls / {spidername} see scrapy docs for details

此方法的附加好处是,可以随时暂停和恢复爬网。 我的蜘蛛现在运行超过11天,阻止~15GB内存(磁盘FIFO队列的文件缓存)

答案 1 :(得分:2)

如果你单独yield每个项目,Python解释器会以不同的方式执行代码:它不再是一个函数,而是一个generator

这样,永远不会创建完整列表,并且当使用生成器的代码要求下一个项目时,每个项目的内存将分配一个。

所以,可能是你没有内存泄漏,你只是分配了大量的内存,大约10k页的时间是列表用于一页的内存。

当然,你仍然可能有真正的内存泄漏,有tips for debugging leaks in Scrapy here

答案 2 :(得分:1)

我想指出罗宾斯答案的最新消息(低回复率,他的帖子现在还不能回复)。

确保对队列使用新语法,因为现在不赞成使用它们的建议。那“ s”使我花了几天的时间才弄清楚出了什么问题。新的语法是这样的:

    DEPTH_PRIORITY = 1 
    SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
    SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'