Leer y escribir archivos XML en Python

L

XML, o Extensible Markup Language, es un lenguaje de marcado que se usa comúnmente para estructurar, almacenar y transferir datos entre sistemas. Aunque no es tan común como solía ser, todavía se usa en servicios como RSS y SOAP, así como para estructurar archivos como documentos de Microsoft Office.

Dado que Python es un lenguaje popular para la web y el análisis de datos, es probable que necesite leer o escribir datos XML en algún momento, en cuyo caso tendrá suerte.

A lo largo de este artículo, principalmente echaremos un vistazo a ElementTree módulo para leer, escribir y modificar datos XML. También lo compararemos con el más antiguo. minidom módulo en las primeras secciones para que pueda obtener una buena comparación de los dos.

Los módulos XML

los minidom, o Implementación mínima de DOM, es una implementación simplificada del Modelo de objetos de documento (DOM). los DOM es una interfaz de programación de aplicaciones que trata XML como una estructura de árbol, donde cada node del árbol es un objeto. Por lo tanto, el uso de este módulo requiere que estemos familiarizados con su funcionalidad.

los ElementTree El módulo proporciona una interfaz más “Pythonic” para manejar XMl y es una buena opción para aquellos que no están familiarizados con DOM. También es probable que sea un mejor candidato para que lo utilicen más programadores novatos debido a su interfaz simple, que verá a lo largo de este artículo.

En este artículo, el ElementTree El módulo se utilizará en todos los ejemplos, mientras que minidom también se demostrará, pero solo para contar y leer documentos XML.

Ejemplo de archivo XML

En los ejemplos siguientes, utilizaremos el siguiente archivo XML, que guardaremos como “items.xml”:

<data>
    <items>
        <item name="item1">item1abc</item>
        <item name="item2">item2abc</item>
    </items>
</data>

Como puede ver, es un ejemplo de XML bastante simple, que solo contiene algunos objetos anidados y un atributo. Sin embargo, debería ser suficiente para demostrar todas las operaciones XML en este artículo.

Leer documentos XML

Usando minidom

Para analizar un documento XML usando minidom, primero debemos importarlo desde el xml.dom módulo. Este módulo utiliza el parse función para crear un objeto DOM a partir de nuestro archivo XML. los parse La función tiene la siguiente sintaxis:

xml.dom.minidom.parse(filename_or_file[, parser[, bufsize]])

Aquí, el nombre del archivo puede ser una cadena que contenga la ruta del archivo o un objeto de tipo de archivo. La función devuelve un documento, que puede manejarse como un tipo XML. Por tanto, podemos utilizar la función getElementByTagName() para encontrar una etiqueta específica.

Dado que cada node puede tratarse como un objeto, podemos acceder a los atributos y al texto de un elemento utilizando las propiedades del objeto. En el siguiente ejemplo, hemos accedido a los atributos y al texto de un node específico y de todos los nodes juntos.

from xml.dom import minidom

# parse an xml file by name
mydoc = minidom.parse('items.xml')

items = mydoc.getElementsByTagName('item')

# one specific item attribute
print('Item #2 attribute:')
print(items[1].attributes['name'].value)

# all item attributes
print('nAll attributes:')
for elem in items:
    print(elem.attributes['name'].value)

# one specific item's data
print('nItem #2 data:')
print(items[1].firstChild.data)
print(items[1].childNodes[0].data)

# all items data
print('nAll item data:')
for elem in items:
    print(elem.firstChild.data)

El resultado es el siguiente:

$ python minidomparser.py 
Item #2 attribute:
item2

All attributes:
item1
item2

Item #2 data:
item2abc
item2abc

All item data:
item1abc
item2abc

Figura 1

Si quisiéramos usar un archivo ya abierto, simplemente podemos pasar nuestro objeto de archivo a parse al igual que:

datasource = open('items.xml')

# parse an open file
mydoc = parse(datasource)

Además, si los datos XML ya estaban cargados como una cadena, podríamos haber usado el parseString() función en su lugar.

Usando ElementTree

ElementTree nos presenta una forma muy sencilla de procesar archivos XML. Como siempre, para poder utilizarlo primero debemos importar el módulo. En nuestro código usamos el import comando con el as palabra clave, que nos permite usar un nombre simplificado (ET en este caso) para el módulo en el código.

Después de la importación, creamos una estructura de árbol con el parse función, y obtenemos su elemento raíz. Una vez que tenemos acceso al node raíz, podemos recorrer fácilmente el árbol, porque un árbol es un gráfico conectado.

Utilizando ElementTree, y como en el ejemplo de código anterior, obtenemos los atributos del node y el texto usando los objetos relacionados con cada node.

El código es el siguiente:

import xml.etree.ElementTree as ET
tree = ET.parse('items.xml')
root = tree.getroot()

# one specific item attribute
print('Item #2 attribute:')
print(root[0][1].attrib)

# all item attributes
print('nAll attributes:')
for elem in root:
    for subelem in elem:
        print(subelem.attrib)

# one specific item's data
print('nItem #2 data:')
print(root[0][1].text)

# all items data
print('nAll item data:')
for elem in root:
    for subelem in elem:
        print(subelem.text)

El resultado será el siguiente:

$ python treeparser.py 
Item #2 attribute:
item2

All attributes:
item1
item2

Item #2 data:
item2abc

All item data:
item1abc
item2abc

Figura 2

Como puede ver, esto es muy similar al minidom ejemplo. Una de las principales diferencias es que el attrib object es simplemente un objeto de diccionario, lo que lo hace un poco más compatible con otro código de Python. Tampoco necesitamos usar value para acceder al valor del atributo del artículo como lo hicimos antes.

Es posible que haya notado cómo acceder a objetos y atributos con ElementTree es un poco más Pythonic, como mencionamos antes. Esto se debe a que los datos XML se analizan como simples listas y diccionarios, a diferencia de minidom donde los elementos se analizan como personalizados xml.dom.minidom.Attr y “nodes de texto DOM”.

Contando los elementos de un documento XML

Usando minidom

Como en el caso anterior, el minidom debe ser importado del dom módulo. Este módulo proporciona la función getElementsByTagName, que usaremos para encontrar el elemento de etiqueta. Una vez obtenido, utilizamos el len() método incorporado para obtener el número de subelementos conectados a un node. El resultado obtenido del código siguiente se muestra en la Figura 3.

from xml.dom import minidom

# parse an xml file by name
mydoc = minidom.parse('items.xml')

items = mydoc.getElementsByTagName('item')

# total amount of items
print(len(items))
$ python counterxmldom.py
2

figura 3

Tenga en cuenta que esto solo contará la cantidad de elementos secundarios debajo de la nota que ejecute len() on, que en este caso es el node raíz. Si desea encontrar todos los subelementos en un árbol mucho más grande, deberá recorrer todos los elementos y contar cada uno de sus elementos secundarios.

Usando ElementTree

Del mismo modo, el ElementTree El módulo nos permite calcular la cantidad de nodes conectados a un node.

Código de ejemplo:

import xml.etree.ElementTree as ET
tree = ET.parse('items.xml')
root = tree.getroot()

# total amount of items
print(len(root[0]))

El resultado es el siguiente:

$ python counterxml.py
2

Figura 4

Escribir documentos XML

Usando ElementTree

ElementTree también es ideal para escribir datos en archivos XML. El siguiente código muestra cómo crear un archivo XML con la misma estructura que el archivo que usamos en los ejemplos anteriores.

Los pasos son:

  • Cree un elemento, que actuará como nuestro elemento raíz. En nuestro caso, la etiqueta de este elemento es “datos”.
  • Una vez que tenemos nuestro elemento raíz, podemos crear subelementos usando el SubElement función. Esta función tiene la sintaxis:

SubElement(parent, tag, attrib={}, **extra)

Aquí parent es el node padre al que conectarse, attrib es un diccionario que contiene los atributos del elemento, y extra son argumentos de palabras clave adicionales. Esta función nos devuelve un elemento, que puede usarse para adjuntar otros subelementos, como lo hacemos en las siguientes líneas al pasar elementos al SubElement constructor.
3. Aunque podemos agregar nuestros atributos con el SubElement función, también podemos utilizar la set() función, como lo hacemos en el siguiente código. El texto del elemento se crea con el text propiedad de la Element objeto.
4. En las últimas 3 líneas del código siguiente, creamos una cadena a partir del árbol XML y escribimos esos datos en un archivo que abrimos.

Código de ejemplo:

import xml.etree.ElementTree as ET

# create the file structure
data = ET.Element('data')
items = ET.SubElement(data, 'items')
item1 = ET.SubElement(items, 'item')
item2 = ET.SubElement(items, 'item')
item1.set('name','item1')
item2.set('name','item2')
item1.text="item1abc"
item2.text="item2abc"

# create a new XML file with the results
mydata = ET.tostring(data)
myfile = open("items2.xml", "w")
myfile.write(mydata)

La ejecución de este código dará como resultado un nuevo archivo, “items2.xml”, que debería ser equivalente al archivo “items.xml” original, al menos en términos de la estructura de datos XML. Probablemente notará que la cadena resultante es solo una línea y no contiene sangría, sin embargo.

Encontrar elementos XML

Usando ElementTree

los ElementTree módulo ofrece el findall() función, que nos ayuda a encontrar elementos específicos en el árbol. Devuelve todos los artículos con la condición especificada. Además, el módulo tiene la función find(), que devuelve solo el primer subelemento que coincide con los criterios especificados. La sintaxis de ambas funciones es la siguiente:

findall(match, namespaces=None)
find(match, namespaces=None)

Para ambas funciones el match El parámetro puede ser un nombre de etiqueta XML o una ruta. La función findall() devuelve una lista de elementos y find devuelve un solo objeto de tipo Element.

Además, hay otra función auxiliar que devuelve el texto del primer node que coincide con el criterio dado:

findtext(match, default=None, namespaces=None)

Aquí hay un código de ejemplo para mostrarle exactamente cómo operan estas funciones:

import xml.etree.ElementTree as ET
tree = ET.parse('items.xml')
root = tree.getroot()

# find the first 'item' object
for elem in root:
    print(elem.find('item').get('name'))

# find all "item" objects and print their "name" attribute
for elem in root:
    for subelem in elem.findall('item'):
    
        # if we don't need to know the name of the attribute(s), get the dict
        print(subelem.attrib)      
    
        # if we know the name of the attribute, access it directly
        print(subelem.get('name'))

Y aquí está la reutilización de ejecutar este código:

$ python findtree.py 
item1
{'name': 'item1'}
item1
{'name': 'item2'}
item2

Figura 5

Modificar elementos XML

Usando ElementTree

los ElementTree El módulo presenta varias herramientas para modificar documentos XML existentes. El siguiente ejemplo muestra cómo cambiar el nombre de un node, cambiar el nombre de un atributo y modificar su valor, y cómo agregar un atributo adicional a un elemento.

El texto de un node se puede cambiar especificando el nuevo valor en el campo de texto del objeto de node. El nombre del atributo se puede redefinir utilizando el set(name, value) función. los set La función no tiene que trabajar solo en un atributo existente, también se puede usar para definir un nuevo atributo.

El siguiente código muestra cómo realizar estas operaciones:

import xml.etree.ElementTree as ET

tree = ET.parse('items.xml')
root = tree.getroot()

# changing a field text
for elem in root.iter('item'):
    elem.text="new text"

# modifying an attribute
for elem in root.iter('item'):
    elem.set('name', 'newitem')

# adding an attribute
for elem in root.iter('item'):
    elem.set('name2', 'newitem2')

tree.write('newitems.xml')

Después de ejecutar el código, el archivo XML resultante “newitems.xml” tendrá un árbol XML con los siguientes datos:

<data>
    <items>
        <item name="newitem" name2="newitem2">new text</item>
        <item name="newitem" name2="newitem2">new text</item>
    </items>
</data>

Como podemos ver al comparar con el archivo XML original, los nombres de los elementos del elemento han cambiado a “nuevo elemento”, el texto a “nuevo texto” y el atributo “nombre2” se ha agregado a ambos nodes.

También puede notar que escribir datos XML de esta manera (llamar tree.write con un nombre de archivo) agrega más formato al árbol XML para que contenga nuevas líneas y sangría.

Creación de subelementos XML

Usando ElementTree

los ElementTree El módulo tiene más de una forma de agregar un nuevo elemento. La primera forma en que veremos es usando el makeelement() función, que tiene el nombre del node y un diccionario con sus atributos como parámetros.

La segunda forma es a través del SubElement() class, que toma el elemento padre y un diccionario de atributos como entradas.

En nuestro ejemplo a continuación, mostramos ambos métodos. En el primer caso, el node no tiene atributos, por lo que creamos un diccionario vacío (attrib = {}). En el segundo caso, usamos un diccionario poblado para crear los atributos.

import xml.etree.ElementTree as ET

tree = ET.parse('items.xml')
root = tree.getroot()

# adding an element to the root node
attrib = {}
element = root.makeelement('seconditems', attrib)
root.append(element)

# adding an element to the seconditem node
attrib = {'name2': 'secondname2'}
subelement = root[0][1].makeelement('seconditem', attrib)
ET.SubElement(root[1], 'seconditem', attrib)
root[1][0].text="seconditemabc"

# create a new XML file with the new element
tree.write('newitems2.xml')

Después de ejecutar este código, el archivo XML resultante se verá así:

<data>
    <items>
        <item name="item1">item1abc</item>
        <item name="item2">item2abc</item>
    </items>
    <seconditems>
         <seconditem name2="secondname2">seconditemabc</seconditem>
    </seconditems>
</data>

Como podemos ver al comparar con el archivo original, se han agregado el elemento “seconditems” y su subelemento “seconditem”. Además, el node “seconditem” tiene “name2” como atributo, y su texto es “seconditemabc”, como se esperaba.

Eliminar elementos XML

Usando ElementTree

Como probablemente esperaría, el ElementTree El módulo tiene la funcionalidad necesaria para eliminar los atributos y subelementos del node.

Eliminar un atributo

El siguiente código muestra cómo eliminar el atributo de un node mediante el pop() función. La función se aplica a la attrib parámetro de objeto. Especifica el nombre del atributo y lo establece en None.

import xml.etree.ElementTree as ET

tree = ET.parse('items.xml')
root = tree.getroot()

# removing an attribute
root[0][0].attrib.pop('name', None)

# create a new XML file with the results
tree.write('newitems3.xml')

El resultado será el siguiente archivo XML:

<data>
    <items>
        <item>item1abc</item>
        <item name="item2">item2abc</item>
    </items>
</data>

Como podemos ver en el código XML anterior, el primer elemento no tiene el atributo “nombre”.

Eliminar un subelemento

Un subelemento específico se puede eliminar usando el remove función. Esta función debe especificar el node que queremos eliminar.

El siguiente ejemplo nos muestra cómo usarlo:

import xml.etree.ElementTree as ET

tree = ET.parse('items.xml')
root = tree.getroot()

# removing one sub-element
root[0].remove(root[0][0])

# create a new XML file with the results
tree.write('newitems4.xml')

El resultado será el siguiente archivo XML:

<data>
    <items>
        <item name="item2">item2abc</item>
    </items>
</data>

Como podemos ver en el código XML anterior, ahora solo hay un node de “elemento”. El segundo se ha eliminado del árbol original.

Eliminar todos los subelementos

los ElementTree módulo nos presenta el clear() función, que se puede utilizar para eliminar todos los subelementos de un elemento dado.

El siguiente ejemplo nos muestra cómo usar clear():

import xml.etree.ElementTree as ET

tree = ET.parse('items.xml')
root = tree.getroot()

# removing all sub-elements of an element
root[0].clear()

# create a new XML file with the results
tree.write('newitems5.xml')

El resultado será el siguiente archivo XML:

<data>
    <items />
</data>

Como podemos ver en el código XML anterior, todos los subelementos del elemento “elementos” se han eliminado del árbol.

Terminando

Python ofrece varias opciones para manejar archivos XML. En este artículo hemos revisado el ElementTree módulo, y lo usó para analizar, crear, modificar y eliminar archivos XML. También hemos utilizado el minidom modelo para analizar archivos XML. Personalmente, recomiendo usar el ElementTree módulo ya que es mucho más fácil trabajar con él y es el módulo más moderno de los dos.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad