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.







