28

这最终会消耗我所有可用的内存,然后进程被杀死。我尝试将标签从schedule“更小”标签更改为“更小”的标签,但这并没有什么不同。

我在做什么错/如何处理这个大文件iterparse()

import lxml.etree

for schedule in lxml.etree.iterparse('really-big-file.xml', tag='schedule'):
    print "why does this consume all my memory?"

我可以很容易地把它切成小块并处理它,但这比我想要的更难看。

4

3 回答 3

33

iterparse迭代整个文件时,会构建一棵树,并且不会释放任何元素。这样做的好处是元素会记住它们的父元素,并且您可以形成引用祖先元素的 XPath。缺点是会消耗大量内存。

为了在解析时释放一些内存,请使用 Liza Daly 的fast_iter

def fast_iter(context, func, *args, **kwargs):
    """
    http://lxml.de/parsing.html#modifying-the-tree
    Based on Liza Daly's fast_iter
    http://www.ibm.com/developerworks/xml/library/x-hiperfparse/
    See also http://effbot.org/zone/element-iterparse.htm
    """
    for event, elem in context:
        func(elem, *args, **kwargs)
        # It's safe to call clear() here because no descendants will be
        # accessed
        elem.clear()
        # Also eliminate now-empty references from the root node to elem
        for ancestor in elem.xpath('ancestor-or-self::*'):
            while ancestor.getprevious() is not None:
                del ancestor.getparent()[0]
    del context

然后你可以像这样使用它:

def process_element(elem):
    print "why does this consume all my memory?"

context = lxml.etree.iterparse('really-big-file.xml', tag='schedule', events=('end',))
fast_iter(context, process_element)

我强烈推荐以上内容所依据的文章fast_iter;如果您正在处理大型 XML 文件,这对您来说应该特别有趣。

上面介绍的fast_iter是文章中所示版本的略微修改版本。这个更激进地删除以前的祖先,从而节省更多内存。在这里,您将找到一个演示差异的脚本。

于 2012-08-28T14:06:48.097 回答
6

直接复制自http://effbot.org/zone/element-iterparse.htm

请注意,iterparse 仍然会构建树,就像 parse 一样,但您可以在解析时安全地重新排列或删除树的某些部分。例如,要解析大文件,您可以在处理完元素后立即删除它们:

for event, elem in iterparse(source):
    if elem.tag == "record":
        ... process record elements ...
        elem.clear()

上述模式有一个缺点;它不会清除根元素,因此您最终会得到一个包含许多空子元素的元素。如果您的文件很大,而不仅仅是很大,这可能是个问题。要解决此问题,您需要掌握根元素。最简单的方法是启用开始事件,并保存对变量中第一个元素的引用:

# get an iterable
context = iterparse(source, events=("start", "end"))

# turn it into an iterator
context = iter(context)

# get the root element
event, root = context.next()

for event, elem in context:
    if event == "end" and elem.tag == "record":
        ... process record elements ...
        root.clear()
于 2012-08-28T14:12:11.847 回答
0

这对我来说非常有效:

def destroy_tree(tree):
    root = tree.getroot()

    node_tracker = {root: [0, None]}

    for node in root.iterdescendants():
        parent = node.getparent()
        node_tracker[node] = [node_tracker[parent][0] + 1, parent]

    node_tracker = sorted([(depth, parent, child) for child, (depth, parent)
                           in node_tracker.items()], key=lambda x: x[0], reverse=True)

    for _, parent, child in node_tracker:
        if parent is None:
            break
        parent.remove(child)

    del tree
于 2018-03-06T21:09:19.000 回答