2011-01-14 31 views
13

tengo un xml de esta manera:manera eficiente para iterar throught de elementos XML

<a> 
    <b>hello</b> 
    <b>world</b> 
</a> 
<x> 
    <y></y> 
</x> 
<a> 
    <b>first</b> 
    <b>second</b> 
    <b>third</b> 
</a> 

necesito iterar a través de todos los <a> y <b> etiquetas, pero no sé cuántos de ellos están en el documento. Así que uso para manejar xpath que:

from lxml import etree 

doc = etree.fromstring(xml) 

atags = doc.xpath('//a') 
for a in atags: 
    btags = a.xpath('b') 
    for b in btags: 
      print b 

Funciona, pero tengo archivos muy grandes, y me muestra que cProfilexpath es muy caro para el uso.

Me pregunto, ¿tal vez exista una forma más eficiente de iterar indefinidamente en el número de elementos xml?

+1

favor traducir "bastante grande" en megabytes. –

Respuesta

17

XPath debe ser rápido. Se puede reducir el número de XPath llama a uno:

doc = etree.fromstring(xml) 
btags = doc.xpath('//a/b') 
for b in btags: 
    print b.text 

Si eso no es lo suficientemente rápido, podría intentar Liza Daly's fast_iter. Esto tiene la ventaja de no requerir que todo el XML se procese primero con etree.fromstring, y los nodos principales se descartan después de que se hayan visitado los elementos secundarios. Ambas cosas ayudan a reducir los requisitos de memoria. Debajo está a modified version of fast_iter, que es más agresivo para eliminar otros elementos que ya no se necesitan.

def fast_iter(context, func, *args, **kwargs): 
    """ 
    fast_iter is useful if you need to free memory while iterating through a 
    very large XML file. 

    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(elt): 
    print(elt.text) 

context=etree.iterparse(io.BytesIO(xml), events=('end',), tag='b') 
fast_iter(context, process_element) 

Liza Daly's article en el análisis de grandes archivos XML puede resultar útil para la lectura también. De acuerdo con el artículo, lxml con fast_iter puede ser más rápido que cElementTree 's iterparse. (Ver la Tabla 1).

+0

¿Cuál es el propósito de 'doc = etree.fromstring (xml)' en el código fast_iter? –

+0

@John Machin: copiar y pegar el error. Gracias por mencionarlo. – unutbu

+0

iterparse speed war: como dice el artículo, lxml es más rápido SI selecciona una etiqueta en particular, y para el análisis general (necesita examinar varias etiquetas), cElementTree es más rápido. –

10

¿Qué tal iter?

>>> for tags in root.iter('b'):   # root is the ElementTree object 
...  print tags.tag, tags.text 
... 
b hello 
b world 
b first 
b second 
b third 
+0

Ese enlace está muerto; aquí hay uno en vivo: http://lxml.de/tutorial.html#tree-iteration –

5

Uso iterparse:

import lxml.etree as ET 
    for event, elem in ET.iterparse(filelike_object): 
     if elem.tag == "a": 
      process_a(elem) 
      for child in elem: 
       process_child(child) 
      elem.clear() # destroy all child elements 
     elif elem.tag != "b": 
      elem.clear() 

Tenga en cuenta que esta no guarda toda la memoria, pero yo he sido capaz de vadear corrientes XML de más de un Gb utilizando esta técnica.

Trate import xml.etree.cElementTree as ET ... se trata con Python y su iterparse es más rápido que el lxml.etreeiterparse, según the lxml docs:

"" "Para aplicaciones que requieren un alto rendimiento analizador de archivos de gran tamaño, y que hacen poco sin serialización, cET es la mejor opción. También para aplicaciones iterparse que extraen pequeñas cantidades de datos o agregan información de grandes conjuntos de datos XML que no caben en la memoria. Sin embargo, si se trata del rendimiento de ida y vuelta, lxml tiende a ser varias veces más rápido en total. Por lo tanto, siempre que los documentos de entrada no sean considerablemente más grandes que la salida, lxml es el claro ganador. "" "

-2

bs4 es muy útil para este

from bs4 import BeautifulSoup 
raw_xml = open(source_file, 'r') 
soup = BeautifulSoup(raw_xml) 
soup.find_all('tags') 
Cuestiones relacionadas