10. Tratamiento de archivos XML con Python

El formato XML se ha introducido en todos los campos (incluidos la traducción) por varios motivos, pero podemos destacar unos pocos:

  • Es un formato estándar y abierto, con lo que se pueden crear documentos para cualquier tipo de aplicación.
  • Hay toda una serie de herramientas estándar que permiten tratar archivos XML de una manera rápida y fácil. En este sentido, Python también dispone de muchas librerías y módulos que permiten crear y leer archivos XML de una manera muy sencilla.

En esta unidad veremos algunos ejemplos sencillos y el lector podrá crearse sus propias aplicaciones modificando los ejemplos que exponemos. También se puede encontrar mucha más información sobre Python y XML en los siguientes enlaces:

Utilizaremos diversas técnicas para leer y obtener información de archivos XML:

  • Funciones de cadenas. Nos servirán en las casos en que las búsquedas sean sencillas y en las que el archivo XML guarde un formato muy homogéneo.
  • Expresiones regulares: nos permitirán realizar operaciones sencillas sobre XMLs. En aplicaciones reales sólo se debe escoger esta opción cuando queramos obtener una información muy concreta de los archivos. Para tratamientos más complejos, es mejor usar las otras opciones que encontrarás en esta misma unidad.
  • El paquete xmltodict que nos transforma un archivo XML en un diccionario de Python.
  • Uso de la librería xml.etree, que nos proporciona muchas funciones para el tratamiento de archivos XML.

En toda esta unidad utilizaremos como archivo XML uno que representa una base de datos de CDs de música. Se puede encontrar adjunto en esta página (y en todo el resto de páginas de la unidad donde haga falta). Aprovechad para abrirlo con un editor de textos. Este archivo lo presentamos en tres versiones:

  • catalog.xml: Es la versión básica, si lo abres verás que presenta un formato muy homogéneo.
  • catalog-mod.xml: Es el mismo archivo y contiene la misma información, pero ahora la información de title presenta un salto de línea adicional.
  • catalog-mod2.xml: Es el mismo archivo XML, pero toda la información está en una sola línea.

Para cada programa que presente será interesante observar si la estrategia que hemos utilizado funciona correctamente para las tres versiones de este archivo.

De esta unidad dispones de los siguientes archivos:


10.1. Tratamiento de archivos XML con funciones de cadena

Los archivos XML se pueden tratar con funciones estándar de cadena, como en el programa-10-1.py.

import codecs

entrada=codecs.open("catalog.xml","r",encoding="utf-8")
sortida=codecs.open("catalog.txt","w",encoding="utf-8")
artist=""
title=""
year=""
for linia in entrada:
    linia=linia.rstrip().lstrip()
    if linia.startswith("</cd>"):
        if not artist=="" and not title=="" and not year=="":
            cadena=artist+"\t"+title+"\t"+year
            print(cadena)
            sortida.write(cadena+"\n")
    if linia.startswith("<title>") and linia.endswith("</title>"):
        title=linia[7:-8]
    elif linia.startswith("<artist>") and linia.endswith("</artist>"):
        artist=linia[8:-9]
    elif linia.startswith("<year>") and linia.endswith("</year>"):
        year=linia[6:-7] 

Si nos fijamos, en este código veremos que sólo se utilizan funciones de cadena estándar como startswith () y endswith (), por ejemplo. Si lo ejecutamos, veremos que funciona correctamente y que a la salida nos da la información de artista, título y año.

Bob Dylan Empire Burlesque 1985
Bonnie Tyler Hide your heart 1988
Dolly Parton Greatest Hits 1982
Gary Moore Still got the blues 1990

Esta estrategia puede ser válida, pero se basa demasiado en la disposición física de las marcas y la información en el documento. Prueba ahora si este programa funciona bien para catalog-mod.txt y catalog-mod2.txt.


10.2. Tratamiento de archivos XML mediante expresiones regulares

Para evitar el problema de la dependencia de la disposición de la información, podemos hacer uso de expresiones regulares, como en el siguiente programa (programa-10-2.py)

import codecs
import re
entrada=codecs.open("catalog.xml","r",encoding="utf-8")
sortida=codecs.open("catalog.txt","w",encoding="utf-8")
artist=""
title=""
year=""
for linia in entrada:
    linia=linia.rstrip().lstrip()
    if linia.startswith("</cd>"):
        if not artist=="" and not title=="" and not year=="":
            cadena=artist+"\t"+title+"\t"+year
            print(cadena)
            sortida.write(cadena+"\n")
    else:
        m_title = re.search('<title>(.+?)</title>', linia)
        if m_title:
            title = m_title.group(1)
        m_artist = re.search('<artist>(.+?)</artist>', linia)
        if m_artist:
            artist = m_artist.group(1)
        m_year = re.search('<year>(.+?)</year>', linia)
        if m_year:
            year = m_year.group(1)

Intenta ejecutar el programa con los archivos catalog modificados. ¿Los puede tratar todos?

10.3. Tratamiento de archivos XML com xmltodict

xml2dict nos permite tratar archivos XML de una manera muy fácil, ya que convierte los archivos XML en una estructura de datos de tipo diccionario. En el programa-10-3.py podemos observar cómo utilizar esta librería.

import xmltodict

xml=open('catalog.xml')
xmldict = xmltodict.parse(xml.read())


for cd in xmldict["catalog"]["cd"]:
    print(cd["artist"],cd["title"],cd["year"])

Prueba con todas las modificaciones del archivo catalog y observa si es capaz de procesar correctamente todos los archivos.

10.4. Tratamiento de archivos XML con xml.etree

Disponemos de una serie de librerías que nos facilitan mucho la lectura de archivos XML. Una de ellas es xml.etree.ElementTree. Para observar cómo funciona, ejecutaremos el programa-10-4.py y observaremos la salida:

import xml.etree.ElementTree as etree

for event, elem in etree.iterparse("catalog.xml",events=("start", "end")):
    print(event,elem,elem.tag,elem.attrib)

La librería es capaz de detectar cuando hay un evento (y hemos seleccionado el principio (start) y el final (end)), el elemento afectado, la etiqueta del elemento y el atributo del elemento. En este programa de prueba simplemente escribimos esta información:

start <Element 'catalog' at 0x7f456b5c3728> catalog {}
start <Element 'cd' at 0x7f4569d999f8> cd {'id': '1'}
start <Element 'title' at 0x7f4569d3c2c8> title {}
end <Element 'title' at 0x7f4569d3c2c8> title {}
start <Element 'artist' at 0x7f4569d3c318> artist {}
end <Element 'artist' at 0x7f4569d3c318> artist {}

Con esta información, podemos hacer un programa que lea el archivo (programa-10.5.py):

import xml.etree.ElementTree as etree
import codecs
artist=""
title=""
year=""
sortida=codecs.open("catalog.txt","w",encoding="utf-8")
for event, elem in etree.iterparse("catalog.xml",events=("start", "end")):
    if event=="end" and elem.tag=="cd":
        cadena=artist+"\t"+title+"\t"+year
        print(cadena)
        sortida.write(cadena+"\n")
        artist=""
        title=""
        year=""
    if event=="end" and elem.tag=="title":
        title="".join(elem.itertext()).lstrip().rstrip()
    if event=="end" and elem.tag=="artist":
        artist="".join(elem.itertext()).lstrip().rstrip()
    if event=="end" and elem.tag=="year":
        year="".join(elem.itertext()).lstrip().rstrip()

Comprueba que este programa funciona también bien con el catalog modificado.