为什么lxml.etree.iterparse()吃了我所有的记忆?(Why is lxml.etr

2019-06-17 21:30发布

这最终消耗了我的所有可用内存,然后进程被杀死。 我试图更改标签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?"

我可以很容易地切开它,并在较小的块处理它,但是这是丑陋的,比我想。

Answer 1:

作为iterparse在整个文件迭代树建并没有元素被释放。 这样做的优点是元素记住他们的父母是谁,你可以形成参考祖先元素的XPath。 其缺点是,它会占用大量的内存。

为了释放为你解析一些内存,使用丽莎达利的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上面介绍的是在文章中示出的一个略加修改的版本。 这是一个更积极的关于删除以前的祖先,从而节省更多的内存。 在这里,你会发现一个脚本这表明了差异。



Answer 2:

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

需要注意的是iterparse仍然建立一棵树,就像解析,但您可以安全地重新排列或删除部分树的同时解析。 例如,解析大文件,你可以得到,只要你处理他们摆脱元素:

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()


Answer 3:

这个工作对我很好:

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


文章来源: Why is lxml.etree.iterparse() eating up all my memory?