expat no Python: parser XML de baixo nível

Publicado em: 23/08/2026
Tempo de leitura: 5 minutos
A laptop screen showing a code editor with visible programming code in a dimly lit environment.

O módulo xml.parsers.expat oferece acesso direto ao parser XML Expat. Ele é rápido, orientado a eventos e não valida documentos contra schemas. Em vez de construir uma árvore automaticamente, a aplicação registra funções para eventos como início de elemento, texto, comentários, namespaces e declarações.

Esse nível de controle é útil em protocolos, ferramentas de análise, conversores e integrações que precisam de desempenho ou callbacks específicos. Para a maioria das tarefas de aplicação, ElementTree no Python ou xml.sax no Python oferecem interfaces mais simples.

Criar um parser

Use ParserCreate(). O parâmetro opcional encoding sobrescreve o encoding declarado no XML. Expat oferece suporte nativo a um conjunto restrito, incluindo UTF-8, UTF-16, ISO-8859-1 e ASCII.

from xml.parsers import expat

parser = expat.ParserCreate()

Uma instância deve processar apenas um documento. Para o próximo arquivo, crie outro parser.

Registrar handlers

Handlers são atribuídos diretamente aos atributos do parser.

from xml.parsers import expat

def inicio(nome, atributos):
    print("início", nome, atributos)

def fim(nome):
    print("fim", nome)

def texto(dados):
    if dados.strip():
        print("texto", repr(dados))

parser = expat.ParserCreate()
parser.StartElementHandler = inicio
parser.EndElementHandler = fim
parser.CharacterDataHandler = texto
parser.Parse("<raiz><item id='1'>Olá</item></raiz>", True)

O segundo argumento de Parse() deve ser verdadeiro na chamada final. Ele informa ao parser que não haverá mais dados e permite verificar tokens incompletos.

Parsing incremental

O documento pode ser enviado em blocos. Isso ajuda com arquivos grandes e streams, mas não limita o volume total automaticamente.

MAX_BYTES = 20 * 1024 * 1024
recebidos = 0

with open("dados.xml", "rb") as arquivo:
    while bloco := arquivo.read(64 * 1024):
        recebidos += len(bloco)
        if recebidos > MAX_BYTES:
            raise ValueError("XML grande demais")
        parser.Parse(bloco, False)

parser.Parse(b"", True)

Defina também limites de eventos, profundidade, texto acumulado e tempo. Ler em blocos evita uma alocação única, mas não impede ataques de expansão.

CharacterDataHandler pode fragmentar texto

Expat pode chamar CharacterDataHandler várias vezes para um único trecho lógico, especialmente em quebras de linha ou limites de blocos. Acumule os fragmentos até o fim do elemento.

partes = []

parser.buffer_text = True

def texto(dados):
    partes.append(dados)

buffer_text=True reduz callbacks, mas ainda não é uma garantia de um callback por elemento. O atributo buffer_size controla o tamanho do buffer.

Namespaces

Passe um separador de um caractere para ParserCreate() a fim de ativar namespaces.

parser = expat.ParserCreate(namespace_separator=" ")

def inicio(nome, atributos):
    namespace, _, local = nome.partition(" ")
    print(namespace, local)

Com namespaces ativados, nomes são expandidos como URI, separador e nome local. Evite depender do prefixo textual original.

Posição atual e erros

Durante callbacks, CurrentLineNumber, CurrentColumnNumber e CurrentByteIndex indicam a posição do evento. Após um erro, use ErrorLineNumber, ErrorColumnNumber, ErrorByteIndex e ErrorCode.

from xml.parsers import expat

try:
    parser.Parse("<raiz><item></raiz>", True)
except expat.ExpatError as erro:
    mensagem = expat.ErrorString(erro.code)
    print(mensagem, erro.lineno, erro.offset)

Não registre o documento inteiro em produção. Inclua apenas origem, posição e uma janela pequena quando ela não contiver segredos.

Atributos ordenados

Por padrão, os atributos chegam como dicionário. Com ordered_attributes=True, chegam como lista alternando nome e valor na ordem do documento.

parser.ordered_attributes = True

Use isso apenas quando a ordem da fonte for necessária para diagnóstico ou reprodução. A ordem de atributos não deve carregar significado semântico em XML.

Atributos especificados

specified_attributes=True faz o parser relatar somente atributos presentes no documento, omitindo valores derivados de declarações. Esse comportamento exige conhecimento das regras de DTD e raramente é necessário em aplicações comuns.

Entidades externas

ExternalEntityRefHandler permite carregar entidades externas, mas pode abrir acesso a arquivos locais e rede. Para XML controlado por usuários, não implemente um handler que abra o systemId.

O artigo de xmlreader no Python mostra como bloquear fontes externas em parsers SAX. A regra é a mesma: o documento não deve escolher recursos.

Entidades de parâmetro e DTD

SetParamEntityParsing() controla entidades de parâmetro. UseForeignDTD() permite solicitar uma DTD alternativa. Essas funções ampliam a superfície de ataque e devem permanecer desativadas em documentos não confiáveis.

Reparse deferral

Expat 2.6 introduziu “reparse deferral” para evitar custo quadrático ao receber tokens muito grandes em fragmentos. No Python, SetReparseDeferralEnabled(False) desativa a proteção e pode reintroduzir negação de serviço.

if hasattr(parser, "GetReparseDeferralEnabled"):
    assert parser.GetReparseDeferralEnabled()

Mantenha a proteção ativada. A consequência é que um handler talvez não seja chamado imediatamente após cada Parse(); isso é esperado.

Proteção contra billion laughs

No Python 3.14.6, parsers Expat podem oferecer métodos para configurar o limiar de ativação e o fator máximo de amplificação de entidades:

if hasattr(parser, "SetBillionLaughsAttackProtectionActivationThreshold"):
    parser.SetBillionLaughsAttackProtectionActivationThreshold(8 * 1024 * 1024)

if hasattr(parser, "SetBillionLaughsAttackProtectionMaximumAmplification"):
    parser.SetBillionLaughsAttackProtectionMaximumAmplification(100.0)

Os valores padrão dependem da biblioteca Expat. Limites muito baixos podem bloquear documentos legítimos. Faça testes com os formatos reais e nunca aumente valores sem compreender a ameaça.

Proteção de memória

Também existem métodos de rastreamento de alocação em versões recentes:

if hasattr(parser, "SetAllocTrackerActivationThreshold"):
    parser.SetAllocTrackerActivationThreshold(64 * 1024 * 1024)

if hasattr(parser, "SetAllocTrackerMaximumAmplification"):
    parser.SetAllocTrackerMaximumAmplification(100.0)

Esses controles complementam, mas não substituem, limites externos de bytes, tempo, profundidade e quantidade de eventos.

Comentários, CDATA e instruções

O parser possui handlers para comentários, início e fim de CDATA, instruções de processamento e declarações XML. CharacterDataHandler recebe tanto texto normal quanto conteúdo CDATA; os handlers de CDATA servem para diferenciar a forma sintática.

GetInputContext()

Durante um callback, GetInputContext() pode retornar o trecho de entrada relacionado ao evento. Use apenas para diagnóstico controlado. O conteúdo pode incluir dados sensíveis ou ser grande.

ParseFile()

ParseFile() recebe um objeto com método read(nbytes). É simples, mas oferece menos controle direto sobre contagem de bytes e cancelamento do que um loop explícito com Parse().

Erro de encoding

Se o XML declara um encoding não suportado ou contém bytes inválidos, Expat gera ExpatError. Não force um encoding incorreto para “fazer funcionar”. Corrija a origem ou converta os bytes em uma etapa bem definida.

Testes recomendados

Teste parsing completo e fragmentado, texto dividido, namespaces, encodings, documento truncado, tag incompatível, atributo duplicado, tokens muito grandes, entidades internas, DTD, entidades externas bloqueadas, profundidade excessiva e limites de amplificação.

Erros comuns

Os erros mais frequentes são reutilizar um parser para vários documentos, esquecer isfinal=True, assumir texto em um único callback, desativar reparse deferral, implementar entidades externas sem política, confiar somente nas proteções internas e ignorar limites de aplicação.

Quando escolher outra API

Use Expat diretamente quando precisar de callbacks de baixo nível, desempenho e controle fino. Para árvores, prefira ElementTree. Para uma interface SAX padronizada, use xml.sax. Para DOM parcial, veja pulldom no Python.

Conclusão

xml.parsers.expat oferece um parser XML rápido e detalhado, mas exige que a aplicação gerencie estado, texto fragmentado, limites e segurança. Mantenha reparse deferral e proteções de amplificação ativas, não carregue entidades externas e crie uma instância por documento.

Consulte a documentação oficial de Expat no Python e o projeto oficial Expat.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementInclude no Python: use XInclude

    Aprenda ElementInclude no Python para usar XInclude com loaders seguros, base URL, profundidade máxima e bloqueio de caminhos externos.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlreader no Python: controle parsers SAX

    Aprenda xmlreader no Python para configurar parsers SAX, InputSource, parsing incremental, atributos, locators e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    saxutils no Python: utilitários para XML

    Aprenda saxutils no Python para escapar XML, preparar atributos, gerar documentos, criar filtros SAX e evitar erros de contexto.

    Ler mais

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

    pulldom no Python: DOM parcial para XML

    Aprenda pulldom no Python para processar XML por eventos, expandir apenas subárvores necessárias e reduzir memória com segurança.

    Ler mais

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

    xml.sax no Python: processe XML em eventos

    Aprenda xml.sax no Python para processar XML por eventos com baixo uso de memória, namespaces, handlers, limites e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    minidom no Python: manipule XML com DOM

    Aprenda xml.dom.minidom no Python para ler, navegar, criar e serializar XML com DOM, namespaces, memória e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026