2012-08-28 36 views
15

這最終會消耗我所有可用的內存,然後該進程被終止。我試過將標籤從schedule更改爲「更小」的標籤,但這沒有什麼區別。爲什麼lxml.etree.iterparse()吃掉了我所有的記憶?

我在做什麼錯誤/如何處理這個大文件與iterparse()

import lxml.etree 

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

我可以很容易地將其切割成小塊,但這比我想要的更醜。

回答

18

由於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) 

我強烈建議the article在其上面fast_iter基於;如果您正在處理大型XML文件,那麼您應該特別感興趣。

上面介紹的fast_iter是文章中顯示的 的一個稍作修改的版本。這個刪除以前的祖先更積極, 從而節省更多的內存。 Here you'll find a script它演示了 的區別。

+0

謝謝!您的解決方案和我剛剛添加的解決方案似乎都有訣竅,我很好奇您和其他人都認爲哪種解決方案更好。你有什麼想法嗎? –

+3

原來你的解決方案有效並且http://effbot.org/zone/element-iterparse.htm解決方案沒有(它仍然吃掉了我所有的記憶) –

+0

謝謝!這是真正有效的版本。 Liza Daly,effbot和lxml官方文檔的版本並沒有爲我節省太多內存。 – fjsj

3

直接從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() 
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 
相關問題