lxml的iterparse尝试将整个文件加载到内存中

1 投票
3 回答
988 浏览
提问于 2025-04-18 05:13

我正在尝试解析一个非常大的XML文件,所以我决定使用lxml.iterparse这个方法,具体可以参考这里

我的代码大概是这样的:

import sys
from lxml import etree

def fast_iter(context, func):
    for event, elem in context:
        func(elem)
        elem.clear()
        while elem.getprevious() is not None:
            del elem.getparent()[0]
    del context

def launchArticleProcessing(elem):
    print elem

context = etree.iterparse(sys.argv[1], events=('end',), tag='text')

fast_iter(context, launchArticleProcessing)

我这样调用它:python lxmlwtf.py "/path/to/my/file.xml"

但是内存一下子就被占满了(直到我不得不终止这个进程,因为这个文件根本装不下),而且什么都没有打印出来。我到底漏掉了什么呢?

3 个回答

0

根据我的经验,定期调用垃圾回收器可以帮助很多。

你可以试试下面这样的代码:

import sys
from lxml import etree

def fast_iter(context, func):
    for i, (event, elem) in enumerate(context):
        # Garbage collect after every 100 elements
        if i % 100 == 0:
            gc.collect()

        func(elem)
        elem.clear()
        while elem.getprevious() is not None:
            del elem.getparent()[0]
    del context

def launchArticleProcessing(elem):
    print elem

context = etree.iterparse(sys.argv[1], events=('end',), tag='text')

fast_iter(context, launchArticleProcessing)
2

我在这里回答过一个非常相似的问题:lxml和fast_iter占用所有内存。主要原因是因为 lxml.etree 会把所有没有被明确捕获的元素都保存在内存中。所以你需要手动清理这些内容。

我做的事情是没有过滤你想要的标签的事件:

context = etree.iterparse(open(filename,'r'),events=('end',))

然后手动解析并清理其他内容:

for (event,elem) in progress.bar(context):
    if elem.tag == 'text':
        # do things here

    # every element gets cleared here
    elem.clear()
    while elem.getprevious() is not None:
        del elem.getparent()[0]
del context
1

我之前说错了,正如我在评论中解释的那样。lxml会把文件加载到内存中,直到找到一个和给定标签对应的项目。

如果找不到这个标签(比如因为lxml在标签前加了命名空间),它就会一直把文件加载到内存中,这就是问题所在。

所以解决办法就是提供一个正确的标签!我通过在文件的一部分上使用普通解析器找到了正确的标签。

撰写回答