Manera eficiente de iterar a través de elementos xml

Tengo un xml como este:

 hello world      first second third  

Necesito recorrer todas las tags y , pero no sé cuántas de ellas están en el documento. Así que uso xpath para manejar eso:

 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 bastante grandes, y cProfile me muestra que xpath es muy caro de usar.

Me pregunto, ¿tal vez hay una forma más eficiente de iterar a través de un número indefinido de elementos xml?

XPath debería ser rápido. Puede reducir el número de llamadas XPath a una:

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

Si eso no es lo suficientemente rápido, puedes probar el fast_iter de Liza Daly . Esto tiene la ventaja de no requerir que todo el XML se procese con etree.fromstring primero, y que los nodos principales se etree.fromstring después de que se hayan visitado los etree.fromstring . Ambas cosas ayudan a reducir los requisitos de memoria. A continuación se muestra una versión modificada de fast_iter que es más agresiva para eliminar otros elementos que ya no son necesarios.

 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) 

El artículo de Liza Daly sobre el análisis de grandes archivos XML puede ser una lectura útil para usted también. Según el artículo, lxml con fast_iter puede ser más rápido que el cElementTree de iterparse . (Ver Tabla 1).

¿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 

Utilice 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 esto no guarda toda la memoria, pero he podido vadear las secuencias XML de más de un Gb usando esta técnica.

Intente import xml.etree.cElementTree as ET … viene con Python y su iterparse es más rápido que lxml.etree iterparse , de acuerdo con la documentación de lxml :

“” “Para las aplicaciones que requieren un alto rendimiento del analizador de archivos grandes y que hacen poca o ninguna serialización, cET es la mejor opción. También para aplicaciones iterparse que extraen pequeñas cantidades de datos o información agregada de grandes conjuntos de datos XML que no lo hacen. 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, cuando los documentos de entrada no son considerablemente más grandes que el resultado, lxml es el claro ganador. “” ”

bs4 es muy útil para esto

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