2012-10-09 16 views
18

Ich muss XML-Dokumente behandeln, die groß genug sind (bis zu 1 GB) und analysieren sie mit Python. Ich benutze die iterparse() Funktion (SAX Style Parsing).ElementTree iterparse Strategie

Meine Sorge ist die folgende, stellen Sie sich eine XML haben wie diese

<?xml version="1.0" encoding="UTF-8" ?> 
<families> 
    <family> 
    <name>Simpson</name> 
    <members> 
     <name>Homer</name> 
     <name>Marge</name> 
     <name>Bart</name> 
    </members> 
    </family> 
    <family> 
    <name>Griffin</name> 
    <members> 
     <name>Peter</name> 
     <name>Brian</name> 
     <name>Meg</name> 
    </members> 
    </family> 
</families> 

Das Problem ist natürlich, ist zu wissen, wenn ich einen Namen (wie Simpsons) bin immer und wenn ich den Namen bin immer von einem dieser Familienmitglieder (zum Beispiel Homer)

Was ich bisher gemacht habe, ist die Verwendung von "Schaltern", die mir sagen, ob ich innerhalb eines "Mitglieder" -Tags bin oder nicht, der Code wird so aussehen

import xml.etree.cElementTree as ET 

__author__ = 'moriano' 

file_path = "test.xml" 
context = ET.iterparse(file_path, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 
on_members_tag = False 
for event, elem in context: 
    tag = elem.tag 
    value = elem.text 
    if value : 
     value = value.encode('utf-8').strip() 

    if event == 'start' : 
     if tag == "members" : 
      on_members_tag = True 

     elif tag == 'name' : 
      if on_members_tag : 
       print "The member of the family is %s" % value 
      else : 
       print "The family is %s " % value 

    if event == 'end' and tag =='members' : 
     on_members_tag = False 
    elem.clear() 

Und das funktioniert gut, wie die Ausgabe

The family is Simpson 
The member of the family is Homer 
The member of the family is Marge 
The member of the family is Bart 
The family is Griffin 
The member of the family is Peter 
The member of the family is Brian 
The member of the family is Meg 

Meine Sorge, dass in dem Tag war ich (on_members_tag) vorstellt, mit den wahren xml Beispielen zu wissen, mit diesem (einfachen) Beispiel ist musste er eine zusätzliche Variable erstellen das muss ich behandeln, sie haben mehr verschachtelte Tags.

Beachten Sie auch, dass dies ein sehr reduziertes Beispiel ist. Sie können also davon ausgehen, dass ich möglicherweise mit mehr Tags, mehr inneren Tags und anderen Tag-Namen, Attributen usw. konfrontiert werde.

Also Frage ist. Mache ich hier etwas schrecklich Dummes? Ich habe das Gefühl, dass es eine elegantere Lösung dafür geben muss.

+0

Was werden Sie mit den Daten zu tun? Konstruieren Sie eine Python-Datenstruktur, um alles zu speichern, oder speichern Sie sie während der Iteration in db oder etwas anderes? –

+0

@JanneKarila: Die Daten könnten auf Python-Struktur, speichern in db oder dump in eine Datei gesetzt werden, es würde auf den Prozess abhängen, in diesem Fall können Sie davon ausgehen, dass es in db geschrieben wird –

Antwort

24

Hier ist ein möglicher Ansatz: Wir pflegen eine Pfadliste und spähen rückwärts, um die Elternknoten zu finden.

path = [] 
for event, elem in ET.iterparse(file_path, events=("start", "end")): 
    if event == 'start': 
     path.append(elem.tag) 
    elif event == 'end': 
     # process the tag 
     if elem.tag == 'name': 
      if 'members' in path: 
       print 'member' 
      else: 
       print 'nonmember' 
     path.pop() 
+0

Einfach, elegant und macht den Job . Vielen Dank :) –

+0

Gibt es einen Standardnamen für diesen Ansatz? Ich glaube, dass dieser Ansatz für viele solcher Probleme verwendet wird. Wenn Sie den Namen sagen könnten, kann ich tiefer graben und das verstehen. –

11

pulldom ist dafür hervorragend. Du bekommst einen Saxophon Stream. Sie können den Stream durchlaufen, und wenn Sie einen Knoten finden, an dem Sie interessiert sind, laden Sie diesen Knoten in ein dom-Fragment.

import xml.dom.pulldom as pulldom 
import xpath # from http://code.google.com/p/py-dom-xpath/ 

events = pulldom.parse('families.xml') 
for event, node in events: 
    if event == 'START_ELEMENT' and node.tagName=='family': 
     events.expandNode(node) # node now contains a dom fragment 
     family_name = xpath.findvalue('name', node) 
     members = xpath.findvalues('members/name', node) 
     print('family name: {0}, members: {1}'.format(family_name, members)) 

Ausgang:

family name: Simpson, members: [u'Hommer', u'Marge', u'Bart'] 
family name: Griffin, members: [u'Peter', u'Brian', u'Meg'] 
+0

Dies ist eine sehr nette Lösung, aber ich kann es nicht als eine akzeptierte Antwort geben (ich mag nneonneo's Antwort besser), aber es sieht definitiv wie eine elegante Lösung aus. Vielen Dank! –

+0

Große Antwort. Sehr einfach zu bedienen. erlaubt eine 46 GB XML-Datei zu analysieren –