Procesamiento fácil de XML con Python y Amara
A pesar de que la libreria estándar Python cuenta con herramientas y modulos para el procesamiento de XML con SAX y DOM, muchos programadores han pensado que podrían existir formas más simples de trabajar con XML. Amara es un conjunto de herramientas que sirven para facilitar el procesamiento de XML usando Python. En este manual se da una breve introducción al uso de Amara para dichas tareas.
Instalación de Amara
Podemos instalar Amara de varias formas.
Instalación clásica (sin setuptools)
- Descarga los archivos correspondientes a tu plataforma y versión de Python de ftp://ftp.4suite.org/pub/Amara/
- Ejecutar el instalador. Si se trata de la distribución de código fuente, es necesario ejecutar lo siguiente:
python setup.py install
También hay ejecutables de Windows.
Nota: Amara depende de 4Suite, por lo tanto, si no está instalado el paquete 4Suite hay que seleccionar una distribución allinone.
Instalación con setuptools
- Si no tienes instalado setuptools puedes instalarlo descargando el siguiente script ez_setup.py y ejecutando de la línea de comandos:
python ez_setup.py -U setuptools
- Ejecutar
easy_install Amara
De forma automática buscará el módulo más reciente y descargará los módulos necesarios.
Ejemplo 1. Uso de expresiones XPath
Vamos a partir de un archivo input.xml que contiene los datos de prueba que usaremos en los ejemplos. Se trata de un documento XML que contiene datos de clientes.
<?xml version="1.0" encoding="iso-8859-1"?>
<clientes>
<cliente id="ibm" agregado="2003-06-20">
<nombre>International Bussines Machines</nombre>
<direccion>
<calle>8 Siempreviva</calle>
<ciudad>Silicon Valley</ciudad>
<provincia>California</provincia>
</direccion>
<lema>
<emph>Vende todo</emph> lo que puedas
</lema>
</cliente>
<cliente id="bk" agregado="2003-06-10">
<nombre>Burguer King</nombre>
<direccion>
<calle>45 Santa Ursula</calle>
<ciudad>Chicago</ciudad>
<provincia>Illinois</provincia>
</direccion>
<lema>
A la parrilla sabe mejor
</lema>
</cliente>
<!-- Agregar 10,000 registros como este -->
<cliente id="so" agregado="2004-11-01">
<nombre>Sony</nombre>
<direccion>
<calle>10 Takataka
</calle>
<ciudad>Tokyo</ciudad>
<provincia>Tokyo</provincia>
</direccion>
</cliente>
</clientes>
El siguiente programa utiliza la expresión XPath /clientes/cliente para obtener los datos de todos los clientes que aparecen en el documento.
Posteriormente se obtiene el nombre y ciudad de cada uno, y se imprimen a la consola. Los datos de entrada son le dos del archivo indicado por el parámetro source:
import amara for fragDom in amara.pushdom(source='input.xml', xpatterns=u"/clientes/cliente"): label = fragDom.firstChild #Obtener el primer hijo nombre = label.xpath('string(nombre)') #obtener el elemento nombre ciudad = label.xpath('string(direccion/ciudad)') #obtener el elemento ciudad que forma parte de la direccion print nombre, 'de', ciudad
Nota: XPath es un lenguaje que permite seleccionar subconjuntos de un documento XML.
La salida del siguiente programa es:
International Bussines Machines de Silicon Valley Burguer King de Chicago Sony de Tokyo
Si se teme que el consumo de memoria sea muy elevado para documentos muy grandes, como ocurre con DOM, no hay nada de que preocuparse, pues domtools.pushdom es un generador que proporciona un fragmento del documento DOM a cada pasada del ciclo, de manera que el documento original no es procesado entero, sino en una serie de sub árboles de acuerdo al patrón /clientes/cliente proporcionado.
Ejemplo 2: Uso de bindings
El siguiente ejemplo simplifica mucho las cosas. Después de leer y analizar el archivo de entrada (input.xml), Amara crea un conjunto de objetos Python que reflejan la estructura del documento XML (a este proceso se le llama binding). Con ello el documento puede ser procesado usando instrucciones Python. El objeto contenedor representa el nodo raíz del documento en cuestión.{{{ #!python import amara contenido = amara.parse('input.xml') #se hace binding del archivo for cli in contenido.clientes.cliente: #para cada cliente
print cli.nombre, 'de', cli.direccion.ciudad}}} La salida es la misma que la del primer ejemplo:
International Bussines Machines de Silicon Valley Burguer King de Chicago Sony de Tokyo
Ejemplo 3: Binding eficiente
A pesar de su sencillez, el ejemplo anterior podría presentar problemas de eficiencia en el uso de memoria al tratar con documentos XML grandes, ya que se crear a una gran cantidad de objetos Python para poder representar el documento. Es por ello que Amara ofrece una forma mas adecuada de realizar algo similar, tomando el enfoque usado por el primer ejemplo. El siguiente código hace uso de la función pushbind, que es un generador de sub árboles que evita tener que crear de una sola vez los objetos:
import amara for subArbol in amara.pushbind(source='input.xml', xpatterns=u'/clientes/cliente'): print subArbol.nombre, 'de', subArbol.direccion.ciudad
La salida de la ejecución del programa anterior seria:
International Bussines Machines de Silicon Valley Burguer King de Chicago Sony de Tokyo
Ejemplo 4: Modificación de Documentos XML
El siguiente ejemplo agrega un nuevo elemento <lema> al cliente con el identificador id="so", y cambia su fecha de última modificación.
import amara import datetime contenedor = amara.parse('input.xml') #Se va a agregar este lema a Sony texto_nuevo_lema = u'Life is good.' #id de Sony id = 'so' #se obtiene una lista de clientes que tengan el id deseado cliente_sony = [ label for label in contenedor.clientes.cliente if label.id == 'so' ] #sabemos que solo hay uno cliente_sony = cliente_sony[0] #Se agrega el nuevo elemento (lema) al final cliente_sony.xml_append(contenedor.xml_create_element(u'lema', None)) #Se agrega de manera sencilla el texto del lema cliente_sony.lema.xml_children.append(texto_nuevo_lema) #Se cambia la fecha de modificacion de ese cliente cliente_sony.agregado = unicode(datetime.date.today()) #se imprime el documento XML ya modificado print contenedor.xml()
La salida obtenida al ejecutarlo es la siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<clientes>
<cliente agregado="2003-06-20" id="ibm">
<nombre>International Bussines Machines</nombre>
<direccion>
<calle>8 Siempreviva</calle>
<ciudad>Silicon Valley</ciudad>
<provincia>California</provincia>
</direccion>
<lema>
<emph>Vende todo</emph> lo que puedas
</lema>
</cliente>
<cliente agregado="2003-06-10" id="bk">
<nombre>Burguer King</nombre>
<direccion>
<calle>45 Santa Ursula</calle>
<ciudad>Chicago</ciudad>
<provincia>Illinois</provincia>
</direccion>
<lema>
A la parrilla sabe mejor
</lema>
</cliente>
<!-- Agregar 10,000 registros como este -->
<cliente agregado="2006-08-09" id="so">
<nombre>Sony</nombre>
<direccion>
<calle>10 Takataka
</calle>
<ciudad>Tokyo</ciudad>
<provincia>Tokyo</provincia>
</direccion>
<lema>
Life is good.
</lema></cliente>
</clientes>
Autor: César Cárdenas Desales
Corregido y actualizado: Luis Miguel Morillas
Basado en el manual de Uche Ogbuji "Introducing the Amara XML Toolkit"
Fecha de Última Actualización: 9 ago 2006
