2012-10-09 16 views
18

Devo gestire documenti xml che sono abbastanza grandi (fino a 1 GB) e li analizziamo con python. Sto usando la funzione iterparse() (analisi stile SAX).ElementTree iterparse strategy

La mia preoccupazione è il seguente, immaginate di avere un XML come questo

<?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> 

Il problema è, naturalmente, per sapere quando sto diventando un nome di famiglia (come Simpson) e quando io sono sempre il nome di uno di quel membro della famiglia (ad esempio Omero)

quello che ho fatto finora è quello di utilizzare "switch", che mi dirà se sono all'interno di un tag "membri" o no, il codice sarà simile a questa

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

E questo funziona bene come l'uscita è

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 

La mia preoccupazione è che con questo (semplice) esempio ho dovuto creare una variabile in più per sapere in quale tag ero (on_members_tag) immaginate con i veri esempi di XML che devo gestire, hanno tag più nidificati.

Si noti inoltre che questo è un esempio molto ridotto, quindi si può presumere che io possa trovarsi di fronte a un xml con più tag, più tag interni e cercando di ottenere nomi di tag diversi, attributi e così via.

Quindi la domanda è. Sto facendo qualcosa di orribilmente stupido qui? Mi sento come se ci dovesse essere una soluzione più elegante a questo.

+0

Che cosa intende fare con i dati? Costruisci una struttura dati Python per tenerlo tutto o memorizzarlo in db mentre iterating o qualcos'altro? –

+0

@JanneKarila: I dati potrebbero essere messi su struttura python, salvare in db o dump in un file, dipenderebbero dal processo, in questo caso si può presumere che sarà scritto in db –

risposta

24

Ecco un possibile approccio: manteniamo un elenco di percorsi e guardiamo all'indietro per trovare i nodi principali.

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

Semplice, elegante e fa il lavoro . Grazie mille :) –

+0

Esiste un nome standard per questo approccio? Credo che questo approccio sia usato per molti di questi problemi. Se tu potessi dire il suo nome, potrei approfondire e capire questo. –

11

pulldom è eccellente per questo. Ottieni un flusso di sax. Puoi scorrere il flusso e quando trovi un nodo a cui sei interessato, carica quel nodo in un frammento dom.

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

uscita:

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

Questa è una soluzione molto bella, tuttavia non posso darti una risposta accettata (mi piace la risposta di nneonneo meglio), tuttavia, sembra decisamente una soluzione elegante. Grazie! –

+0

Ottima risposta. Molto semplice da usare. permesso di analizzare un file xml da 46 GB –

Problemi correlati