2012-08-28 11 views
15

Dies verbraucht schließlich meinen gesamten verfügbaren Speicher und dann wird der Prozess beendet. Ich habe versucht, das Tag von schedule zu "kleineren" Tags zu ändern, aber das machte keinen Unterschied.Warum verbraucht lxml.etree.iterparse() all meinen Speicher?

Was mache ich falsch/wie kann ich diese große Datei mit iterparse() verarbeiten?

Ich kann es leicht schneiden und in kleineren Brocken verarbeiten, aber das ist hässlicher, als ich möchte.

Antwort

18

Da iterparse über die gesamte Datei iteriert, wird ein Baum erstellt und keine Elemente werden freigegeben. Der Vorteil dabei ist, dass die Elemente sich daran erinnern, wer ihr Elternteil ist, und Sie können XPaths bilden, die sich auf Vorfahrelemente beziehen. Der Nachteil ist, dass es viel Speicher verbrauchen kann.

Um etwas Speicher zu frei wie Sie analysieren, verwenden 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 

, die Sie dann wie folgt verwenden:

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) 

I the article, auf dem die oben fast_iter empfehlen basiert; Es sollte für Sie besonders interessant sein, wenn Sie mit großen XML-Dateien arbeiten.

Die oben dargestellte ist eine leicht modifizierte Version der im Artikel gezeigten . Dieser ist aggressiver beim Löschen von vorherigen Vorfahren, spart somit mehr Speicher. Here you'll find a script, die den Unterschied zeigt.

+0

Dank! Sowohl Ihre Lösung als auch die, die ich gerade hinzugefügt habe, scheinen den Trick zu machen, ich bin neugierig, welche Sie und andere Leute fühlen, ist eine bessere Lösung. Hast du irgendwelche Gedanken? –

+3

Stellt sich heraus, dass Ihre Lösung funktioniert und die http://effbot.org/zone/element-iterparse.htm Lösung nicht (es hat immer noch meinen gesamten Speicher aufgefressen) –

+0

Danke! Dies ist die Version, die wirklich funktioniert. Versionen von Liza Daly, effbot und offiziellen Lxml-Dokumenten haben mir NICHT viel Speicher gespart. – fjsj

3

direkt kopiert von http://effbot.org/zone/element-iterparse.htm

Beachten Sie, dass iterparse noch einen Baum aufbaut, genau wie Parse, aber Sie können sicher Teile des Baumes neu anordnen oder entfernen, während das Parsen. Zum Beispiel, große Dateien zu analysieren, Sie loswerden Elemente erhalten können, sobald Sie sie bearbeitet haben:

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

Das obige Muster hat auch einen Nachteil; Das Wurzelelement wird nicht gelöscht. Sie erhalten also ein einzelnes Element mit vielen leeren untergeordneten Elementen. Wenn Ihre Dateien groß sind und nicht nur groß sind, könnte dies ein Problem darstellen. Um dies zu umgehen, müssen Sie das Wurzelelement in die Hände bekommen. Der einfachste Weg, dies zu tun ist Startereignis zu ermöglichen, und einen Verweis auf das erste Element in einer Variablen speichern:

# 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

Das ist für mich wirklich gut funktioniert:

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 
Verwandte Themen