Interpretación en profundidad de varias formas en que Python analiza XML
En términos de análisis XML, Python implementa su principio de "pilas incluidas". En su propia biblioteca estándar, Python proporciona una gran cantidad de paquetes y herramientas que se pueden usar para procesar el lenguaje XML. La cantidad es tan grande que incluso los principiantes en programación de Python no tienen otra opción.
Este artículo presentará una interpretación en profundidad de varias formas de utilizar el lenguaje Python para analizar archivos XML y tomará el módulo ElementTree recomendado por el autor como ejemplo para demostrar métodos y escenarios de uso específicos. La versión de Python utilizada en este artículo es 2.7.
1. ¿Qué es XML?
XML es la abreviatura de Extensible Markup Language (lenguaje de marcado extensible), del cual el marcado es la parte clave. Puede crear contenido y luego etiquetarlo con etiquetas calificadas, haciendo de cada palabra, frase o fragmento de información una pieza de información reconocible y clasificable.
El lenguaje de marcado ha evolucionado gradualmente desde su forma inicial desarrollada por empresas privadas y gobiernos hasta el lenguaje de marcado generalizado estándar (SGML), el lenguaje de marcado de hipertexto (HTML) y finalmente evolucionó hacia XML. XML tiene las siguientes características.
XML está diseñado para transmitir datos, no para mostrarlos.
Las etiquetas XML no están predefinidas. Debe definir las etiquetas usted mismo.
XML está diseñado para ser autodescriptivo.
XML es un estándar recomendado por el W3C.
Actualmente, XML juega un papel tan importante en la Web como HTML, que siempre ha sido la piedra angular de la Web. XML está en todas partes. XML es la herramienta más utilizada para la transferencia de datos entre varias aplicaciones y se está volviendo cada vez más popular en el campo del almacenamiento y descripción de información. Por lo tanto, aprender a analizar archivos XML es muy importante para el desarrollo web.
2. ¿Cuáles son los paquetes de Python que pueden analizar XML?
La biblioteca estándar de Python proporciona 6 paquetes que se pueden utilizar para procesar XML.
xml.dom
xml.dom implementa la API DOM desarrollada por W3C. Utilice este paquete si está acostumbrado a utilizar la API DOM o si alguien le pide que lo haga. Sin embargo, tenga en cuenta que este paquete también proporciona varios módulos diferentes, cada uno con un rendimiento diferente.
El analizador DOM debe colocar en la memoria los datos del árbol generados en función del archivo XML antes de que comience cualquier procesamiento, por lo que el uso de la memoria del analizador DOM se basa completamente en el tamaño de los datos de entrada.
xml.dom.minidom
xml.dom.minidom es una implementación minimalista de la API DOM. Es mucho más simple que la versión completa de DOM y el paquete también es mucho más. menor. Aquellos que no estén familiarizados con el DOM deberían considerar usar el módulo xml.etree.ElementTree. Según la evaluación del autor de lxml, este módulo no es cómodo de usar, no es eficiente y es propenso a tener problemas.
xml.dom.pulldom
A diferencia de otros módulos, el módulo xml.dom.pulldom proporciona un "analizador de extracción". El concepto básico detrás de él se refiere a la extracción de datos de XML Pull. eventos de la secuencia y luego procesarlos. Aunque utiliza el mismo modelo de procesamiento basado en eventos que SAX, la diferencia es que cuando usa el analizador de extracción, el usuario necesita extraer eventos explícitamente del flujo XML y atravesar y procesar estos eventos hasta que se complete el procesamiento o se produzca un error.
El análisis pull es una tendencia reciente en el procesamiento XML. Los marcos de análisis XML anteriormente populares, como SAX y DOM, se basaban en push, lo que significa que el control del trabajo de análisis estaba en manos del analizador.
xml.sax
El módulo xml.sax implementa la API SAX. Este módulo sacrifica la comodidad a cambio de velocidad y uso de memoria. SAX es la abreviatura de Simple API para XML. No es un estándar oficial propuesto por el W3C. Está controlado por eventos y no necesita leer todo el documento a la vez, y el proceso de lectura del documento también es el proceso de análisis SAX. El llamado impulsado por eventos se refiere a un método de ejecución de programa basado en el mecanismo de devolución de llamada.
xml.parser.expat
xml.parser.expat proporciona una interfaz API directa de bajo nivel para el analizador expat escrito en lenguaje C. La interfaz de expatriados es similar a SAX y también se basa en el mecanismo de devolución de llamada de eventos. Sin embargo, esta interfaz no está estandarizada y solo se aplica a la biblioteca de expatriados.
expat es un analizador orientado a transmisiones. Registra una función de devolución de llamada (o controlador) del analizador y luego comienza a buscar en su documentación. Cuando el analizador reconoce la ubicación especificada del archivo, llama al controlador correspondiente para esa parte (si ha registrado uno). El archivo se envía al analizador, donde se divide en varios fragmentos y se carga en la memoria. Entonces el expatriado puede analizar esos archivos enormes.
xml.etree.ElementTree (en lo sucesivo, ET)
El módulo xml.etree.ElementTree proporciona una API Pythonic ligera y una implementación eficiente del lenguaje C, concretamente xml.etree. cElementTree. En comparación con DOM, ET es más rápido y la API es más directa y cómoda de usar. En comparación con SAX, la función ET.iterparse también proporciona funcionalidad de análisis bajo demanda y no lee todo el documento en la memoria a la vez. El rendimiento de ET es más o menos similar al del módulo SAX, pero su API es de nivel superior y más conveniente para los usuarios.
El autor recomienda que cuando se utiliza Python para el análisis XML, el módulo ET sea la primera opción, a menos que tenga otras necesidades especiales, que pueden requerir módulos adicionales para satisfacerlo.
Estas API para analizar XML no son exclusivas de Python. Python también se introduce tomando prestado de otros lenguajes o introduciéndolos directamente desde otros lenguajes. Por ejemplo, expat es una biblioteca de desarrollo desarrollada en lenguaje C y utilizada para analizar documentos XML. SAX fue desarrollado originalmente por David Megginson utilizando el lenguaje Java. DOM puede acceder y modificar el contenido y la estructura de un documento de manera independiente de la plataforma y el lenguaje, y se puede aplicar a cualquier lenguaje de programación.
A continuación, tomamos el módulo ElementTree como ejemplo para presentar cómo analizar lxml en Python.
3. Utilice ElementTree para analizar XML
La biblioteca estándar de Python proporciona dos implementaciones de ET. Una es una implementación pura de Python de xml.etree.ElementTree y la otra es una implementación más rápida en lenguaje C de xml.etree.cElementTree. Recuerda utilizar siempre la implementación en lenguaje C ya que es mucho más rápida y consume mucha menos memoria.
Si la versión de Python que está utilizando no tiene el módulo de aceleración requerido por cElementTree, puede importar el módulo de esta manera:
pruebe:
import xml.etree.cElementTree as ET
excepto ImportError:
importar xml.etree.ElementTree como ET
Si hay diferentes implementaciones de una determinada API, lo anterior es un método de importación común. Eso sí, es posible que si importas el primer módulo directamente, no habrá ningún problema. Tenga en cuenta que desde Python 3.3, no es necesario utilizar el método de importación anterior, porque el módulo ElementTree dará prioridad automáticamente al acelerador de C. Si no existe una implementación de C, se utilizará la implementación de Python. Por lo tanto, los amigos que usan Python 3.3 solo necesitan importar xml.etree.ElementTree.
1. Analizar documentos XML en árboles
Comencemos con lo básico. XML es un formato de datos estructurado y jerárquico, y la estructura de datos más adecuada para incorporar XML es un árbol. ET proporciona dos objetos: ElementTree convierte todo el documento XML en un árbol y Element representa un único nodo en el árbol. La interacción con todo el documento XML (lectura, escritura y búsqueda de los elementos necesarios) generalmente se realiza en el nivel ElementTree. Para un único elemento XML y sus subelementos, se realiza en el nivel de Elemento. A continuación damos ejemplos para presentar los principales métodos de uso.
Utilizamos el siguiente documento XML como datos de demostración:
texto, fuente
xml, sgml
A continuación, cargamos este documento , y analizado:
gt;gt;gt; import xml.etree.ElementTree as ET
gt;gt;gt; tree = ET.ElementTree(file=' doc1.xml ')
Luego, obtenemos el elemento raíz:
gt; gt; tree.getroot()
Como antes Como se mencionó, el elemento raíz (raíz) es un objeto Elemento. Echemos un vistazo a los atributos que tiene el elemento raíz:
gt; gt; root = tree.getroot()
gt; .attrib
('doc', {})
Sí, el elemento raíz no tiene atributos.
Al igual que otros objetos Element, el elemento raíz también tiene una interfaz para atravesar sus elementos secundarios directos:
gt; gt; for child_of_root in root:
... print child_of_root. etiqueta, child_of_root.attrib
...
rama {'hash': '1cdf045c', 'nombre': 'codingpy.com'}
rama { 'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}
También podemos acceder a valores específicos a través de el valor del índice Elementos secundarios:
gt; gt; root[0].tag, root[0].text
('branch', '
text , source
')
2. Encuentre los elementos requeridos
Del ejemplo anterior, es obvio que podemos usar un método recursivo simple. (Para cada elemento An, acceda recursivamente a todos sus elementos secundarios) para obtener todos los elementos del árbol. Sin embargo, dado que se trata de una tarea muy común, ET proporciona algunas formas sencillas de implementarla.
El objeto Elemento tiene un método iter, que puede realizar un recorrido en profundidad (DFS) de todos los subelementos bajo un objeto elemento. Los objetos ElementTree también tienen este método. Esta es la forma más sencilla de encontrar todos los elementos en un documento XML:
gt;gt;gt; for elem in tree.iter():
... print elem.tag , elem.attrib
...
doc {}
branch {'hash': '1cdf045c', 'nombre': 'codingpy.com' }
rama {'hash': 'f200013e', 'nombre': 'release01'}
subrama {'name': 'subrelease01'}
branch {'name': 'invalid'}
Sobre esta base, podemos recorrer el árbol arbitrariamente: recorrer todos los elementos y encontrar los atributos que nos interesan. Pero la ET puede hacer que este trabajo sea más fácil y rápido. El método iter puede aceptar un nombre de etiqueta y luego iterar a través de todos los elementos con la etiqueta proporcionada:
gt; gt; for elem in tree.iter(tag='branch'):
... imprimir elem.tag, elem.attrib
...
rama {'hash': '1cdf045c', 'nombre': 'codingpy.com' }
rama {'hash': 'f200013e', 'nombre': 'release01'}
rama {'nombre': 'inválido'}
3. Admite la búsqueda de elementos mediante XPath
Es más conveniente utilizar XPath para encontrar elementos de interés.
Hay algunos métodos de búsqueda en el objeto Elemento que pueden aceptar rutas XPath como parámetros. El método de búsqueda devolverá el primer subelemento coincidente, findall devuelve todos los subelementos coincidentes en forma de lista e iterfind devuelve un iterador de todos. elementos coincidentes). El objeto ElementTree también tiene estos métodos y, en consecuencia, su búsqueda comienza desde el nodo raíz.
Aquí hay un ejemplo del uso de XPath para buscar elementos:
gt;gt;gt; for elem in tree.iterfind('branch/sub-branch'):
... imprimir elem.tag, elem.attrib
...
subrama {'nombre': 'subrelease01'}
El código anterior devuelve todos los elementos con la etiqueta sub-rama debajo del elemento de rama. A continuación, busque todos los elementos de la rama con un determinado atributo de nombre:
gt; gt; for elem in tree.iterfind('branch[@name="release01"]'):
... print elem.tag, elem.attrib
...
branch {'hash': 'f200013e', 'name': 'release01'} p>
4. Construir un documento XML
Con ET, es fácil completar la construcción de un documento XML y escribirlo y guardarlo como un archivo. El método de escritura del objeto ElementTree puede cumplir este requisito.
En términos generales, existen dos escenarios de uso principales. Una es leer primero un documento XML, realizar modificaciones y luego escribir las modificaciones en el documento, y la otra es crear un nuevo documento XML desde cero.
Para modificar el documento, puede ajustar el objeto Elemento. Por favor vea el siguiente ejemplo:
gt;gt;gt; root = tree.getroot()
gt;gt;gt; del root[2]
gt; gt; gt; root[0].set('foo', 'bar')
gt; para subelem en raíz:
... print subelem.tag, subelem.attrib
...
branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'codingpy. com'}
branch {'hash': 'f200013e', 'name': 'release01'}
En el código anterior, hemos eliminado el tercer elemento secundario del elemento raíz. elemento, añadiendo nuevos atributos al primer elemento hijo. Este árbol se puede reescribir en un archivo. El documento XML final debería verse así:
gt; gt; import sys
gt; p> text, source
xml, sgml
Tenga en cuenta que el orden de los atributos de los elementos en el documento es diferente al del documento original. Esto se debe a que ET guarda atributos en forma de diccionario, y un diccionario es una estructura de datos desordenada. Por supuesto, a XML tampoco le importa el orden de los atributos.
También es fácil crear un documento completo desde cero.
El módulo ET proporciona una función de fábrica SubElement para simplificar el proceso de creación de elementos:
gt; gt; a = ET.Element('elem')
gt; gt;gt; c = ET.SubElement(a, 'child1')
gt;gt;gt; c.text = "algo de texto"
gt;gt;gt ; d = ET.SubElement(a, 'child2')
gt;gt;gt; b = ET.Element('elem_b')
gt;gt;gt; ET.Element('root')
gt;gt;gt; root.extend((a, b))
gt;gt;gt; árbol = ET.ElementTree ( root)
gt; gt; tree.write(sys.stdout)
algo de texto
5. >
Los documentos XML suelen ser relativamente grandes. Si lee el documento directamente en la memoria, surgirán problemas durante el análisis. Esta es una de las razones por las que no se recomienda utilizar DOM sino SAX API.
Como mencionamos anteriormente, ET puede cargar documentos XML como árboles almacenados en la memoria (árbol en memoria) y luego procesarlos. Pero al analizar archivos grandes, esto también debería causar el mismo problema de consumo de memoria que DOM, ¿verdad? Sí, este problema existe. Para resolver este problema, ET proporciona una herramienta especial similar a SAX: iterparse, que puede analizar XML secuencialmente.
A continuación, el autor le mostrará cómo utilizar iterparse y compararlo con el método de análisis de árbol estándar. Usamos un documento XML generado automáticamente. Aquí está el comienzo del documento:
Estados Unidos
1
duteous nine dieciocho
. Tarjeta de crédito
[...]
Contemos cuántos elementos de ubicación cuyo valor de texto es Zimbabwe aparecen en el documento. Esta es la forma estándar de usar ET.parse:
tree = ET.parse(sys.argv[2])
count = 0
for elem en tree.iter(tag='ubicación'):
if elem.text == 'Zimbabwe':
recuento = 1
recuento de impresiones p >
El código anterior cargará todos los elementos en la memoria y los analizará uno por uno. Al analizar un documento XML de aproximadamente 100 MB, el uso de memoria del proceso Python que ejecuta el script anterior alcanzó un máximo de aproximadamente 560 MB, con un tiempo de ejecución total de 2,9 segundos.
Tenga en cuenta que en realidad no necesitamos cargar todo el árbol en la memoria. Simplemente detecta que el texto es el elemento de ubicación correspondiente. Todos los demás datos se pueden descartar.
En este momento, podemos usar el método iterparse:
count = 0
para evento, elem en ET.iterparse(sys.argv[2]):
if event == 'end':
if elem.tag == 'ubicación' y elem.text == 'Zimbabue':
count = 1
elem.clear() # Descartar el elemento
recuento de impresiones
El bucle for anterior atravesará el evento iterparse, primero verificará si el evento finaliza y luego determinará el etiqueta del elemento Si es una ubicación y si su valor de texto coincide con el valor objetivo. Además, llamar a elem.clear() es muy crítico: porque iterparse seguirá generando un árbol, solo de forma secuencial. Descartar elementos innecesarios equivale a descartar todo el árbol y liberar memoria asignada por el sistema.
Cuando se utiliza el script anterior para analizar el mismo archivo, el uso máximo de memoria es de solo 7 MB y el tiempo de ejecución es de 2,5 segundos. La razón de la mejora de la velocidad es que solo atravesamos el árbol una vez cuando está construido. El método estándar de utilizar el análisis es completar la construcción de todo el árbol antes de recorrerlo nuevamente para encontrar los elementos requeridos.
El rendimiento de iterparse es comparable al de SAX, pero su API es más útil: iterparse construye el árbol secuencialmente; cuando usa SAX, debe completar la construcción del árbol usted mismo.