2012-10-09 23 views
18

Tengo que manejar documentos xml que sean lo suficientemente grandes (hasta 1GB) y analizarlos con python. Estoy utilizando la función iterparse() (análisis de estilo SAX).ElementTree iterparse strategy

Mi preocupación es la siguiente, imagine que tiene un xml como esto

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

El problema es, por supuesto, para saber cuando estoy recibiendo un nombre familiar (Simpson) y cuando estoy recibiendo el nombre de uno de ese miembro de la familia (por ejemplo, Homero)

Lo que he estado haciendo hasta ahora es usar "interruptores" que me digan si estoy dentro de una etiqueta de "miembros" o no, el código se verá así

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

Y esto funciona bien como la salida es

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 

Mi preocupación es que con este ejemplo (simple) que tenía que crear una variable adicional para saber en qué etiqueta que estaba (on_members_tag) imaginar con los verdaderos ejemplos xml que tengo que manejar, tienen más etiquetas anidadas.

También tenga en cuenta que este es un ejemplo muy reducido, por lo que puede suponer que puedo enfrentar un xml con más etiquetas, más etiquetas internas y tratando de obtener diferentes nombres de etiquetas, atributos, etc.

Así que la pregunta es. ¿Estoy haciendo algo terriblemente estúpido aquí? Siento que debe haber una solución más elegante para esto.

+0

¿Qué vas a hacer con los datos? ¿Construir una estructura de datos Python para mantener todo, o almacenar en db mientras se itera, o algo más? –

+0

@JanneKarila: Los datos se pueden poner en estructura python, guardar en db o volcar en un archivo, dependerá del proceso, en este caso se puede suponer que se escribirá en db –

Respuesta

24

Aquí hay un enfoque posible: mantenemos una lista de rutas y echamos un vistazo hacia atrás para encontrar los nodos principales.

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

Simple, elegante y hace el trabajo . Muchas gracias :) –

+0

¿Hay algún nombre estándar para este enfoque? Creo que este enfoque se usa para muchos de esos problemas. Si pudieras decir el nombre, puedo profundizar más y entender esto. –

11

pulldom es excelente para esto. Obtienes una transmisión de saxofón. Puede iterar a través de la secuencia, y cuando encuentre un nodo que le interese, cargue ese nodo en un fragmento 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)) 

de salida:

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

Esta es una solución muy buena, sin embargo no puedo darle una respuesta aceptada (me gusta más la respuesta de nneonneo), sin embargo, definitivamente parece una solución elegante. ¡Gracias! –

+0

Gran respuesta. Muy simple de usar permitido analizar un archivo XML de 46 GB –

Cuestiones relacionadas