2012-08-28 9 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) 

私は非常にfast_iter上記のどのthe articleをお勧めしますにもとづいて;大規模な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

が直接iterparseはまだちょうど解析のように、ツリーを構築http://effbot.org/zone/element-iterparse.htm

ノートからコピーされますが、解析時に、あなたは安全に木の部分を再配置または削除することができます。たとえば、大きなファイルを解析するために、あなたはすぐにあなたがそれらを処理してきたように要素を取り除くことができます。

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 
関連する問題