lxml解析器消耗所有内存

12 投票
3 回答
4692 浏览
提问于 2025-04-16 13:26

我正在用Python写一个爬虫,使用lxml库来解析HTML,使用gevent库来实现异步操作。我发现经过一段时间后,lxml解析器开始消耗内存,最高能达到8GB(也就是服务器的全部内存)。但我只有100个异步线程,每个线程解析的文档最大也就300KB。

我测试过,发现问题出现在lxml.html.fromstring这个函数上,但我无法重现这个问题。

问题出在这行代码:

HTML = lxml.html.fromstring(htmltext)

也许有人知道这可能是什么原因,或者怎么解决这个问题?

谢谢大家的帮助。

附言:

Linux Debian-50-lenny-64-LAMP 2.6.26-2-amd64 #1 SMP Tue Jan 25 05:59:43 UTC 2011 x86_64    GNU/Linux
Python : (2, 6, 6, 'final', 0)
lxml.etree : (2, 3, 0, 0)
libxml used : (2, 7, 8)
libxml compiled : (2, 7, 8)
libxslt used : (1, 1, 26)
libxslt compiled : (1, 1, 26)

更新:

我为使用lxml解析器的进程设置了内存限制,分别是ulimit -Sv 500000和ulimit -Sm 615000。

现在过了一段时间后,它们开始在错误日志中写入:

“异常 MemoryError: MemoryError() 在 'lxml.etree._BaseErrorLog._receive' 被忽略。”

而且我无法捕捉到这个异常,所以它会不断在日志中写这个消息,直到磁盘上有空闲空间。

我该如何捕捉这个异常,以便终止进程,让守护进程可以创建一个新的进程呢?

3 个回答

0

看起来这个问题是因为lxml这个库依赖的另一个库引起的,那个库叫libxml2,是用C语言写的。这里有个最早的报告:http://codespeak.net/pipermail/lxml-dev/2010-December/005784.html。这个bug在lxml v2.3的修复日志和libxml2的更新日志里都没有提到。

哦,这里还有后续的邮件讨论:https://bugs.launchpad.net/lxml/+bug/728924

我试着重现这个问题,但没有发现什么异常。能够重现这个问题的人可能能帮助澄清一下情况。

1

有一篇很棒的文章,地址是 http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks,这篇文章展示了如何用图形化的方式调试内存结构;这可能会帮助你找出哪些东西没有被释放,以及为什么没有被释放。

补充:我找到了我之前提到的那个链接的文章 - Python内存泄漏

7

你可能在保存一些引用,这些引用会让文档一直存在。比如说,使用xpath评估得到的字符串结果要小心:默认情况下,这些字符串是“智能”的,它们可以访问包含它们的元素,因此如果你保留了对这些字符串的引用,整个树结构就会一直保存在内存中。你可以查看关于xpath返回值的文档了解更多信息:

在某些情况下,这种智能字符串的行为并不理想。比如说,这意味着字符串会让整个树结构保持活跃,如果字符串值是树中唯一重要的内容,这可能会占用大量内存。对于这种情况,你可以通过使用关键字参数smart_strings来关闭这种父子关系。

(我不知道这是否是你遇到的问题,但这是一个可能的原因。我自己也曾遇到过这个问题;-))

撰写回答