xmlreader no Python: controle parsers SAX

Publicado em: 23/08/2026
Tempo de leitura: 5 minutos
A developer typing code on a laptop with a Python book beside in an office.

O módulo xml.sax.xmlreader define as interfaces usadas por parsers SAX no Python. Ele descreve como um parser recebe fontes de entrada, entrega eventos a handlers, configura features e properties, informa linha e coluna e trabalha com parsing incremental.

Na prática, a maioria das aplicações cria um parser com xml.sax.make_parser(), mas compreender XMLReader, InputSource, Locator e os objetos de atributos ajuda a controlar segurança, encoding, streams e extensões do parser.

O papel de XMLReader

XMLReader é a interface-base dos parsers SAX. Um driver de parser fornece uma função create_parser(), e make_parser() usa essa função para criar o leitor. O leitor recebe uma fonte XML e emite callbacks para handlers registrados.

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

class MeuHandler(ContentHandler):
    def startElement(self, name, attrs):
        print("início:", name)

parser = make_parser()
parser.setContentHandler(MeuHandler())
parser.parse("dados.xml")

Quando parse() retorna, o documento foi processado por completo. Para trabalhar com blocos que chegam aos poucos, use a interface incremental.

Handlers configuráveis

O leitor pode receber ContentHandler, DTDHandler, EntityResolver e ErrorHandler. Se nenhum ContentHandler for definido, eventos de conteúdo são descartados. Sem um ErrorHandler, erros normalmente viram exceções e warnings podem ser impressos.

O artigo de xml.sax no Python explica os callbacks principais. O módulo saxutils no Python fornece geradores e filtros para o mesmo fluxo.

InputSource para controlar a origem

InputSource encapsula identificador público, identificador de sistema, encoding, stream binário e stream de caracteres. Ele permite entregar ao parser uma fonte já aberta, evitando que o leitor tente abrir URLs ou caminhos sozinho.

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

fonte = InputSource()
fonte.setSystemId("importacao-local")
fonte.setEncoding("utf-8")
fonte.setByteStream(open("dados.xml", "rb"))

parser = make_parser()
parser.setContentHandler(MeuHandler())
parser.parse(fonte)

Quando existe um character stream, o parser ignora byte stream e encoding declarado no InputSource. Quando existe byte stream, ele tem preferência sobre a abertura automática do system ID.

Byte stream ou character stream

Um byte stream deixa o parser interpretar a declaração de encoding XML, a menos que o encoding seja informado explicitamente. Um character stream já contém texto decodificado; portanto, a aplicação assume responsabilidade pelo decoding correto.

Evite decodificar bytes com errors="ignore" antes do parser. Isso pode apagar caracteres e alterar o significado do documento. O guia de codecs no Python aborda políticas de encoding e erros.

Impedir abertura automática de URLs

Passar uma string a parse() pode representar arquivo, path-like ou identificador de sistema. Em cenários com dados externos, prefira abrir a fonte você mesmo, aplicar limites e fornecer o stream. Isso evita que uma referência controlada pelo usuário seja tratada como recurso remoto sem validação.

Combine essa prática com um EntityResolver que rejeite entidades externas. XML externo não deve escolher livremente arquivos locais, URLs ou protocolos.

Features do parser

getFeature() e setFeature() consultam ou alteram opções booleanas conhecidas pelo SAX. Entre elas estão processamento de namespaces, prefixos e entidades externas. Um parser pode não reconhecer ou não suportar determinada 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:
    pass

Não engula exceções silenciosamente em código crítico. Registre se a feature não foi reconhecida e rejeite o processamento quando uma proteção obrigatória não puder ser garantida.

Properties

Properties transportam objetos ou valores mais complexos, como lexical handlers. getProperty() e setProperty() podem gerar SAXNotRecognizedException ou SAXNotSupportedException. Configure tudo antes de iniciar o parsing.

Parsing incremental

IncrementalParser permite alimentar dados com feed(). Depois do último bloco, close() verifica condições de well-formedness que só podem ser confirmadas no fim. Para reutilizar o parser, chame reset() depois de close().

from xml.sax import make_parser

parser = make_parser()
parser.setContentHandler(MeuHandler())

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

parser.close()

Não misture parse() e feed() durante uma operação. Também não chame reset() enquanto o documento estiver sendo processado.

Limites no parsing incremental

O fato de ler em blocos não limita o tamanho total. Conte bytes recebidos, eventos, profundidade e texto acumulado. Interrompa a operação antes de exceder a política.

MAX_BYTES = 20 * 1024 * 1024
lidos = 0

while bloco := origem.read(64 * 1024):
    lidos += len(bloco)
    if lidos > MAX_BYTES:
        raise ValueError("XML excede o tamanho permitido")
    parser.feed(bloco)

parser.close()

Locator para linha e coluna

Um Locator associa eventos à posição atual do documento. O parser entrega o objeto por setDocumentLocator(). Os valores são confiáveis apenas durante callbacks do handler.

from xml.sax.handler import ContentHandler

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

    def startElement(self, name, attrs):
        linha = self.locator.getLineNumber()
        coluna = self.locator.getColumnNumber()
        print(name, linha, coluna)

Copie linha e coluna no momento do evento. Não guarde o locator para consultar depois esperando a posição antiga.

AttributesImpl

AttributesImpl representa atributos recebidos por startElement(). Ele se comporta parcialmente como mapping e oferece getLength(), getNames(), getType() e getValue().

O objeto de atributos pode ser reutilizado pelo parser. Se precisar guardar os dados depois do callback, crie uma cópia.

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

AttributesNSImpl e namespaces

No modo namespace-aware, startElementNS() recebe nomes como tuplas (namespaceURI, localname). AttributesNSImpl também trabalha com essas tuplas e pode converter entre nomes qualificados e pares de namespace.

Não dependa apenas do prefixo textual. Prefixos podem mudar sem alterar a URI. Para interoperabilidade, compare namespace URI e nome local.

ErrorHandler

Um error handler pode tratar warnings, erros recuperáveis e erros fatais. Em integrações de dados, normalmente é melhor interromper em erros estruturais e devolver uma mensagem controlada. Não inclua o documento inteiro em logs.

EntityResolver seguro

Um resolver pode bloquear qualquer entidade externa.

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

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

parser.setEntityResolver(BloqueiaExternos())

Dependendo da política, prefira levantar uma exceção em vez de retornar conteúdo vazio. O importante é não abrir o identificador externo automaticamente.

Testes recomendados

Teste strings, Path, streams binários e streams de texto; encoding declarado e sobrescrito; namespaces; atributos copiados; parser incremental; documento truncado; entidades externas; feature não suportada; limites de bytes e posições do locator.

Erros comuns

Os erros mais frequentes são confiar em system IDs externos, usar character stream com decoding incorreto, esquecer close(), reutilizar parser sem reset(), guardar objetos de atributos sem copiar, ativar entidades externas e assumir que toda feature existe em todos os parsers.

Conclusão

xml.sax.xmlreader define a infraestrutura por trás dos parsers SAX: leitores, fontes, handlers, features, properties, locators e atributos. Conhecer essas interfaces permite controlar melhor entrada, encoding, streaming e segurança.

Abra fontes externas com limites, mantenha entidades externas desativadas e configure o parser antes de iniciar. Consulte a documentação oficial de xmlreader e as orientações de segurança XML do Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026