xmlreader en Python: controla parsers SAX

Publicado el: 23/08/2026
Tempo de leitura: 5 minutos
A person reads 'Python for Unix and Linux System Administration' indoors.

El módulo xml.sax.xmlreader define las interfaces utilizadas por los parsers SAX en Python. Describe cómo un parser recibe fuentes, envía eventos a handlers, configura features y properties, informa línea y columna y admite parsing incremental.

La mayoría de las aplicaciones crea un parser con xml.sax.make_parser(), pero comprender XMLReader, InputSource, Locator y los objetos de atributos permite controlar mejor seguridad, encoding, streams y extensiones.

El papel de XMLReader

XMLReader es la interfaz base de los drivers SAX. Un driver expone una función create_parser(), y make_parser() la usa para crear el lector. El lector procesa XML e invoca los handlers registrados.

from xml.sax import make_parser
from xml.sax.handler import ContentHandler

class MiHandler(ContentHandler):
    def startElement(self, name, attrs):
        print("inicio:", name)

parser = make_parser()
parser.setContentHandler(MiHandler())
parser.parse("datos.xml")

Cuando parse() retorna, el documento fue procesado completamente. Usa la interfaz incremental cuando los bloques llegan poco a poco o no quieres un método bloqueante.

Handlers configurables

El lector puede recibir ContentHandler, DTDHandler, EntityResolver y ErrorHandler. Sin content handler, los eventos de contenido se descartan. Sin error handler, los errores suelen convertirse en excepciones y los warnings pueden imprimirse.

El artículo de xml.sax en Python explica los callbacks. saxutils en Python proporciona generadores y filtros.

Controlar el origen con InputSource

InputSource guarda identificador público, identificador de sistema, encoding, byte stream y character stream. Permite entregar al parser una fuente ya abierta en vez de permitir que abra una ruta o URL automáticamente.

from xml.sax import make_parser
from xml.sax.xmlreader import InputSource

fuente = InputSource()
fuente.setSystemId("importacion-local")
fuente.setEncoding("utf-8")
fuente.setByteStream(open("datos.xml", "rb"))

parser = make_parser()
parser.setContentHandler(MiHandler())
parser.parse(fuente)

Cuando existe un character stream, el parser ignora el byte stream y el encoding del InputSource. Un byte stream tiene prioridad sobre la apertura del system ID.

Byte stream y character stream

Un byte stream permite que el parser inspeccione la declaración de encoding XML, salvo que la aplicación lo indique. Un character stream ya está decodificado, por lo que la aplicación asume la responsabilidad.

No decodifiques bytes XML con errors="ignore". Eliminar caracteres puede cambiar el significado. El guía de codecs en Python explica encodings y políticas de error.

Evitar apertura automática de URLs

Un string pasado a parse() puede representar un archivo, objeto path-like o identificador de sistema. Con datos externos, abre la fuente, aplica límites de tamaño y protocolo y entrega el stream. Así una referencia controlada por usuarios no se convierte en acceso libre a red o archivos.

Combina esta práctica con un EntityResolver que rechace entidades externas. El XML no debería elegir archivos locales, hosts o protocolos.

Features del parser

getFeature() y setFeature() consultan o cambian opciones booleanas SAX. Entre las más conocidas están namespaces, prefijos y entidades externas. Un parser puede no reconocer o no soportar una feature.

from xml.sax import make_parser
from xml.sax.handler import feature_namespaces, feature_external_ges

parser = make_parser()
parser.setFeature(feature_namespaces, True)

try:
    parser.setFeature(feature_external_ges, False)
except Exception as exc:
    raise RuntimeError("No se puede garantizar la protección XML") from exc

No ignores silenciosamente una configuración de seguridad fallida. Si una protección obligatoria no está disponible, rechaza la operación.

Properties

Las properties transportan objetos más complejos, como lexical handlers. getProperty() y setProperty() pueden lanzar SAXNotRecognizedException o SAXNotSupportedException. Configúralas antes de iniciar.

Parsing incremental

IncrementalParser acepta bloques mediante feed(). Después del último bloque, close() verifica condiciones que solo pueden confirmarse al final. Llama a reset() después de close() antes de reutilizar el parser.

from xml.sax import make_parser

parser = make_parser()
parser.setContentHandler(MiHandler())

with open("grande.xml", "rb") as archivo:
    while bloque := archivo.read(64 * 1024):
        parser.feed(bloque)

parser.close()

No mezcles parse() y feed() en la misma operación. No llames a reset() mientras se procesa un documento.

Los límites siguen siendo necesarios

Leer por bloques no limita el tamaño total. Cuenta bytes, eventos, profundidad, texto acumulado y tiempo.

MAX_BYTES = 20 * 1024 * 1024
recibidos = 0

while bloque := origen.read(64 * 1024):
    recibidos += len(bloque)
    if recibidos > MAX_BYTES:
        raise ValueError("El XML supera el límite")
    parser.feed(bloque)

parser.close()

Locator para línea y columna

Un Locator asocia un evento con la posición actual. El parser lo entrega mediante setDocumentLocator(). Los valores son válidos solo durante los callbacks.

from xml.sax.handler import ContentHandler

class HandlerConPosicion(ContentHandler):
    def setDocumentLocator(self, locator):
        self.locator = locator

    def startElement(self, name, attrs):
        linea = self.locator.getLineNumber()
        columna = self.locator.getColumnNumber()
        print(name, linea, columna)

Copia línea y columna durante el evento. Consultar el locator más tarde dará otra posición.

AttributesImpl

AttributesImpl representa los atributos pasados a startElement(). Implementa parte del protocolo de mapping y métodos como getLength(), getNames(), getType() y getValue().

El parser puede reutilizar el objeto. Copia los atributos si deben sobrevivir al callback.

def startElement(self, name, attrs):
    copia = dict(attrs.items())
    self.registros.append((name, copia))

Atributos con namespaces

Con namespaces activados, startElementNS() recibe nombres como tuplas (namespaceURI, localname). AttributesNSImpl usa la misma forma y convierte entre nombres cualificados y pares de namespace.

No dependas solo del prefijo textual. Puede cambiar mientras la URI permanece igual.

Manejo de errores

Un error handler recibe warnings, errores recuperables y fatales. En pipelines de datos suele ser mejor detenerse ante errores estructurales y devolver un diagnóstico controlado. Evita registrar el XML completo o credenciales.

Resolver que bloquea entidades

from io import StringIO
from xml.sax.handler import EntityResolver
from xml.sax.xmlreader import InputSource

class BloquearExternas(EntityResolver):
    def resolveEntity(self, publicId, systemId):
        fuente = InputSource()
        fuente.setCharacterStream(StringIO(""))
        return fuente

parser.setEntityResolver(BloquearExternas())

Según la aplicación, lanzar una excepción puede ser más seguro que devolver contenido vacío. La regla esencial es evitar acceso externo automático.

Pruebas recomendadas

Prueba strings, objetos Path, byte streams, character streams, encoding declarado y sobrescrito, namespaces, atributos copiados, entrada incremental, documentos truncados, entidades externas, features no soportadas, límites de bytes y posiciones.

Errores comunes

Los fallos frecuentes son confiar en system IDs externos, decodificar mal character streams, olvidar close(), reutilizar sin reset(), guardar atributos sin copiar, habilitar entidades externas y asumir que todos los parsers soportan las mismas features.

Conclusión

xml.sax.xmlreader define la infraestructura de los parsers SAX: lectores, fuentes, handlers, features, properties, locators y atributos. Comprenderla permite controlar entrada, encoding, streaming y seguridad.

Abre fuentes externas con límites, mantén desactivadas las entidades externas y configura el lector antes del parsing. Consulta la documentación oficial de xmlreader y las orientaciones de seguridad XML de Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    saxutils en Python: utilidades para XML

    Aprende saxutils en Python para escapar XML, preparar atributos, generar documentos, crear filtros SAX y evitar errores de contexto.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pulldom en Python: DOM parcial para XML

    Aprende pulldom en Python para procesar XML por eventos, expandir subárboles selectivos y reducir memoria con límites seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xml.sax en Python: procesa XML por eventos

    Aprende xml.sax en Python para procesar XML por eventos con poca memoria, namespaces, handlers, límites y entidades seguras.

    Ler mais

    Tempo de leitura: 5 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

    minidom en Python: manipula XML con DOM

    Aprende xml.dom.minidom en Python para leer, navegar, crear y serializar XML con nodos DOM, namespaces, memoria y seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    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