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

    Vibrant green snake coiled on a tree branch amidst lush jungle foliage.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree: lee y modifica XML en Python

    Aprende ElementTree en Python para leer, buscar, modificar y generar XML con namespaces, parsing incremental, límites y seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    A retro blue mailbox attached to a vibrant yellow wall, perfect for vintage themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    poplib en Python: lee correos con POP3

    Aprende poplib en Python para acceder a POP3 con TLS, listar y descargar mensajes, usar UIDL, aplicar límites y evitar

    Ler mais

    Tempo de leitura: 6 minutos
    22/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    imaplib en Python: lee correos con IMAP

    Aprende imaplib en Python para acceder a buzones IMAP con TLS, buscar por UID, leer sin marcar mensajes, usar flags

    Ler mais

    Tempo de leitura: 6 minutos
    22/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ftplib en Python: FTP y FTPS seguros

    Aprende ftplib en Python para listar, descargar y subir archivos por FTP o FTPS con TLS, timeouts, límites, reanudación y

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Rack de servidores que representa un endpoint creado con xmlrpc.server en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crea servidores XML-RPC

    Aprende xmlrpc.server en Python para crear servidores XML-RPC, registrar funciones, limitar métodos y rutas y evitar exposición insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Código de error sobre datos binarios que representa fallos manejados con urllib.error en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error en Python: maneja errores HTTP

    Aprende urllib.error en Python para manejar URLError, HTTPError, descargas incompletas, retries selectivos y diagnósticos de red claros.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026