minidom en Python: manipula XML con DOM

Publicado el: 22/08/2026
Tempo de leitura: 5 minutos
Close-up view of a computer screen displaying code in a software development environment.

xml.dom.minidom es una implementación compacta del Document Object Model en la biblioteca estándar de Python. En lugar de centrarse solo en elementos e hijos, DOM representa documentos, elementos, atributos, nodos de texto, comentarios y otros tipos mediante una API similar a la de navegadores y otros lenguajes.

El módulo resulta útil cuando una integración exige conceptos DOM, cuando necesitas manipular nodos individualmente o al portar código de otra plataforma. Para la mayoría de trabajos comunes con XML, la documentación recomienda ElementTree en Python, normalmente más sencillo y eficiente en memoria.

Parsear un archivo

from xml.dom import minidom

with minidom.parse("catalogo.xml") as documento:
    raiz = documento.documentElement
    print(raiz.tagName)

parse() acepta un nombre de archivo u objeto similar a archivo y devuelve un Document. El context manager llama unlink() al salir, permitiendo liberar referencias internas antes.

El parsing construye todo el árbol DOM antes de retornar. No uses minidom con documentos ilimitados o muy grandes sin controles estrictos.

Parsear una cadena

xml = "<producto id='7'><nombre>Teclado</nombre></producto>"

with minidom.parseString(xml) as documento:
    producto = documento.documentElement
    print(producto.getAttribute("id"))

Antes de parseString(), impone un límite de bytes o caracteres. Una fuente XML relativamente pequeña puede producir muchos objetos según su estructura.

Tipos de nodos

Cada nodo expone nodeType. Las constantes comunes incluyen DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE, COMMENT_NODE y PROCESSING_INSTRUCTION_NODE.

from xml.dom import Node

for nodo in producto.childNodes:
    if nodo.nodeType == Node.ELEMENT_NODE:
        print("elemento", nodo.tagName)
    elif nodo.nodeType == Node.TEXT_NODE:
        print("texto", repr(nodo.data))

Los espacios y saltos entre etiquetas también pueden convertirse en nodos de texto. Nunca supongas que el primer hijo es un elemento.

Buscar elementos

productos = documento.getElementsByTagName("producto")

for producto in productos:
    print(producto.getAttribute("id"))

getElementsByTagName() busca recursivamente entre descendientes. Para hijos directos, filtra childNodes. Las búsquedas recursivas repetidas pueden costar tiempo; crea índices de aplicación cuando consultes mucho un documento.

Extraer texto correctamente

Un elemento puede contener varios nodos de texto mezclados con elementos. Define si deseas texto directo o contenido recursivo.

from xml.dom import Node

def texto_directo(elemento: Node) -> str:
    partes = []
    for hijo in elemento.childNodes:
        if hijo.nodeType == Node.TEXT_NODE:
            partes.append(hijo.data)
    return "".join(partes)

Para texto recursivo, recorre descendientes. Decide cómo tratar comentarios, CDATA y espacios en lugar de aplicar una regla implícita.

Atributos

producto.setAttribute("activo", "si")
identificador = producto.getAttribute("id")

if producto.hasAttribute("temporal"):
    producto.removeAttribute("temporal")

getAttribute() devuelve cadena vacía si el atributo no existe. Usa hasAttribute() cuando importe distinguir ausencia de valor vacío.

Crear un documento

from xml.dom.minidom import getDOMImplementation

implementacion = getDOMImplementation()
documento = implementacion.createDocument(None, "catalogo", None)
raiz = documento.documentElement

producto = documento.createElement("producto")
producto.setAttribute("id", "7")
raiz.appendChild(producto)

nombre = documento.createElement("nombre")
nombre.appendChild(documento.createTextNode("Teclado"))
producto.appendChild(nombre)

Crea nodos mediante su Document. No instancies clases internas de minidom directamente.

Añadir y eliminar nodos

precio = documento.createElement("precio")
precio.appendChild(documento.createTextNode("199.90"))
producto.appendChild(precio)

producto.removeChild(precio)
precio.unlink()

removeChild() separa un nodo, pero el objeto puede conservar referencias. unlink() vuelve inutilizable el nodo y sus descendientes y favorece liberar memoria.

Clonar nodos

copia = producto.cloneNode(deep=True)
copia.setAttribute("id", "8")
raiz.appendChild(copia)

Con deep=False, solo se copia el nodo. Revisa identificadores y referencias antes de insertar clones para evitar IDs duplicados.

Namespaces

URI = "https://example.com/catalogo"

documento = implementacion.createDocument(URI, "cat:catalogo", None)
producto = documento.createElementNS(URI, "cat:producto")
documento.documentElement.appendChild(producto)

Usa métodos con sufijo NS, como createElementNS(), getElementsByTagNameNS() y setAttributeNS(). El prefijo es una elección de serialización; la URI identifica el namespace.

Serialización con toxml

datos = documento.toxml(
    encoding="utf-8",
    standalone=True,
)

with open("catalogo.xml", "wb") as archivo:
    archivo.write(datos)

Con encoding explícito, toxml() devuelve bytes. Sin él, devuelve string Unicode. Usa nombres válidos en declaraciones, como UTF-8.

Pretty print

bonito = documento.toprettyxml(
    indent="  ",
    newl="\n",
    encoding="utf-8",
)

toprettyxml() mejora legibilidad, pero puede añadir espacios que cambian texto mixto significativo. No uses pretty print para documentos firmados o comparaciones byte a byte.

writexml

with open("catalogo.xml", "w", encoding="utf-8") as archivo:
    documento.writexml(
        archivo,
        addindent="  ",
        newl="\n",
        encoding="UTF-8",
    )

El writer de writexml() recibe texto, no bytes. Combina correctamente el modo del archivo y la interfaz.

Salida atómica

Escribe en un archivo temporal del mismo directorio, haz flush cuando corresponda y reemplaza la ruta final solo después del éxito. Consulta tempfile en Python.

DOM mantiene relaciones padre-hijo y todo el árbol en memoria. Los documentos grandes pueden consumir mucho más que APIs de streaming. Llama documento.unlink() al terminar o usa with minidom.parse(...) as documento.

Después de unlink, no reutilices nodos. La operación sirve para limpieza, no para reset parcial.

XML externo y seguridad

La documentación remite a las vulnerabilidades XML de Python. La entrada no confiable puede explotar expansión de entidades, profundidad y consumo de recursos. Limita tamaño, actualiza el runtime y considera bibliotecas endurecidas.

No permitas que una URL o ruta controlada por usuario pase directamente a parse(). Separa obtención, validación de destino y parsing para reducir SSRF y lectura local.

Parser SAX configurado

parse() puede recibir un parser SAX2 configurado previamente. Esto permite instalar un entity resolver o features antes del constructor DOM.

import xml.sax
from xml.dom import minidom

parser = xml.sax.make_parser()
# Configura features y resolver antes del parsing.
documento = minidom.parse("datos.xml", parser=parser)

Minidom cambia el document handler y activa namespaces. Prueba la configuración exacta con la versión de Python de producción.

Comentarios e instrucciones

DOM puede preservar comentarios y processing instructions. Trata su contenido como no confiable. Las transformaciones deben decidir explícitamente si conservarlos o eliminarlos.

Minidom frente a ElementTree

ElementTree ofrece una API más Pythonica para la mayoría de XML e incluye procesamiento incremental. Minidom tiene sentido cuando necesitas el modelo completo DOM, tipos de nodo explícitos o compatibilidad con código basado en W3C.

Validación

Parsing no valida la estructura de negocio. Después de construir el DOM, comprueba raíz, namespaces, atributos obligatorios, cardinalidad, tipos, rangos y relaciones. Minidom no valida XSD automáticamente.

Errores

XML malformado suele lanzar una excepción Expat o SAX. Captura errores alrededor del parsing y conserva la causa sin exponer el documento completo.

from xml.parsers.expat import ExpatError

try:
    documento = minidom.parseString(xml_recibido)
except ExpatError as error:
    raise ValueError(
        f"XML inválido en línea {error.lineno}, columna {error.offset}"
    ) from error

Pruebas recomendadas

Prueba nodos de texto con espacios, comentarios, CDATA, atributos ausentes y vacíos, namespaces, clonación profunda, eliminación, pretty print, encoding, documentos grandes, XML malformado, profundidad excesiva, unlink y validación de dominio.

Errores comunes

Los fallos frecuentes son usar el primer childNode sin comprobar tipo, confundir atributo ausente con vacío, retener DOM grandes, olvidar cleanup, aplicar pretty print a texto mixto, crear nodos sin Document, ignorar namespaces, parsear XML ilimitado y tratar parsing como validación.

Conclusión

xml.dom.minidom proporciona una implementación DOM compacta y familiar. Úsala cuando importe el modelo completo de nodos. Para XML directo o archivos grandes, ElementTree o SAX suelen ser mejores.

Consulta la documentación oficial de minidom y la especificación DOM Level 1. Para XML externo, combina límites estrictos, parser actualizado, validación y cleanup explícito.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026