使用iterparse()解析大型XML会消耗太多内存。有其他替代方法吗?

3
我正在使用带有最新lxml库的python 2.7版本。我正在解析一个非常同质化结构和数百万个元素的大型XML文件。我认为lxml的iterparse在解析时不会构建内部树,但显然它确实这样做了,因为内存使用量增加到崩溃(约1GB)。有没有一种方法可以在不使用太多内存的情况下使用lxml解析大型XML文件?
我看到目标解析器接口是一种可能性,但我不确定它是否会更好。
2个回答

2
尝试使用Liza Daly的fast_iter
def fast_iter(context, func, args=[], kwargs={}):
    # http://www.ibm.com/developerworks/xml/library/x-hiperfparse/
    # Author: Liza Daly
    for event, elem in context:
        func(elem, *args, **kwargs)
        elem.clear()
        while elem.getprevious() is not None:
            del elem.getparent()[0]
    del context

"fast_iter"在解析完元素后会从树中移除它们,以及不再需要的之前的元素(可能带有其他标签)。
可以像这样使用它:
import lxml.etree as ET
def process_element(elem):
    ...
context=ET.iterparse(filename, events=('end',), tag=...)        
fast_iter(context, process_element)

1
链接中有一篇很棒的文章。+1 - Steven Rumbalski
Liza的代码有一个陷阱; 它期望唯一的标签名称。如果您嵌套相同的标记,则内部标记将为空。(http://www.willmer.com/kb/2012/02/minor-gotcha-with-liza-dalys-fast_iter/有一个稍微长一些的答案,在此评论中无法编写代码示例) - Rachel
Rachel,我认为“陷阱”出现在你在调用ET.iterparse时使用events = ('start',)并将其传递给fast_iter的情况下。在这种情况下,元素在到达起始标记后而不是到达结束标记后被删除。这可能会导致错误(逻辑或语法)。此代码演示了这个问题。如果这是您看到的错误,则修复方法是将start更改为end - unutbu
如果您要查找的元素在xml流中出现得很远,那么Liza的方法就无济于事了,此时解析器已经消耗了大量内存来构建树形结构。在这种情况下,以下方法效果更佳:http://effbot.org/zone/element-iterparse.htm#incremental-parsing - claymation

0

我曾经遇到过这个问题,并从http://effbot.org/zone/element-iterparse.htm#incremental-parsing中得到了一些提示,最终解决了它:

elems = ET.Element('MyElements')
for event, elem in ET.iterparse(filename):
    if is_needed(elem): # implement this condition however you like
        elems.append(elem)
    else:
        elem.clear()

这将为您提供一棵仅包含所需元素的树,而在解析过程中不需要不必要的内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接