ElementTree: leia e modifique XML no Python

Publicado em: 22/08/2026
Tempo de leitura: 5 minutos
Vibrant green snake coiled on a tree branch amidst lush jungle foliage.

xml.etree.ElementTree oferece uma API simples e eficiente para ler, consultar, modificar e gerar XML. O módulo representa o documento como uma árvore: ElementTree contém o documento e cada Element representa uma tag com atributos, texto e filhos.

ElementTree atende configurações, feeds, integrações legadas, arquivos de projeto e formatos baseados em XML. Porém, a documentação oficial alerta que dados XML não autenticados podem ser perigosos. Aplique limites de tamanho, profundidade e tempo e não habilite recursos externos sem uma política explícita.

Lendo um arquivo XML

import xml.etree.ElementTree as ET

arvore = ET.parse("catalogo.xml")
raiz = arvore.getroot()

print(raiz.tag)
print(raiz.attrib)

parse() retorna um ElementTree. Para uma string pequena já carregada, use fromstring():

xml = "<produto id='7'><nome>Teclado</nome></produto>"
raiz = ET.fromstring(xml)

Não leia um arquivo arbitrariamente grande inteiro antes de impor um limite. Valide tamanho no armazenamento ou na camada HTTP antes de entregar os bytes ao parser.

Tags, atributos, text e tail

Um elemento possui tag, attrib, text e tail. text contém o conteúdo antes do primeiro filho; tail contém texto depois do fechamento do elemento e antes da próxima tag.

for filho in raiz:
    print(filho.tag, filho.get("id"), filho.text, filho.tail)

Para juntar o texto interno de uma subárvore, use "".join(elemento.itertext()). Não presuma que todo texto está somente em .text.

Buscando elementos

for produto in raiz.findall("produto"):
    nome = produto.findtext("nome", default="")
    preco = produto.findtext("preco", default="0")
    print(nome, preco)

find() retorna o primeiro elemento ou None. Faça o teste explícito elemento is None. O teste booleano de elementos vazios está em processo de mudança e já produz aviso de depreciação em versões recentes.

Iteração recursiva

for link in raiz.iter("link"):
    print(link.get("href"))

iter() percorre toda a subárvore em profundidade. Em documentos grandes, limite o número de elementos processados para evitar consumo excessivo.

XPath limitado

ElementTree suporta uma parte reduzida do XPath, suficiente para muitos caminhos, atributos, descendentes e posições.

ativos = raiz.findall(".//produto[@ativo='sim']")
segundo = raiz.findall(".//item[2]")

Não espere funções e expressões completas de um mecanismo XPath dedicado. Também não monte uma expressão com texto arbitrário do usuário. Escolha filtros conhecidos ou valide valores antes de interpolar.

Namespaces

Ao fazer parsing, uma tag com namespace é expandida para o formato {URI}nome.

ns = {
    "atom": "http://www.w3.org/2005/Atom",
}

for entrada in raiz.findall("atom:entry", ns):
    titulo = entrada.findtext("atom:title", namespaces=ns)
    print(titulo)

Use um dicionário de prefixos próprio. O prefixo original no documento não é a identidade do namespace; a URI é.

Criando XML

raiz = ET.Element("catalogo", {"versao": "1"})
produto = ET.SubElement(raiz, "produto", {"id": "7"})
ET.SubElement(produto, "nome").text = "Teclado"
ET.SubElement(produto, "preco").text = "199.90"

ET.indent(raiz, space="  ")
xml = ET.tostring(
    raiz,
    encoding="unicode",
    xml_declaration=False,
)
print(xml)

O serializer faz escaping de texto e atributos. Mesmo assim, valide nomes de tags e estrutura quando forem escolhidos dinamicamente. Não use dados do usuário como nomes de elementos sem uma allowlist.

Escrevendo em arquivo

arvore = ET.ElementTree(raiz)
arvore.write(
    "catalogo.xml",
    encoding="utf-8",
    xml_declaration=True,
)

Grave primeiro em arquivo temporário no mesmo diretório e substitua o destino após sucesso. Consulte tempfile no Python para escrita atômica.

Modificando elementos

for produto in raiz.findall("produto"):
    produto.set("revisado", "sim")
    preco = produto.find("preco")
    if preco is not None:
        preco.text = str(round(float(preco.text or "0"), 2))

Ao remover filhos, primeiro colete os elementos e depois modifique a árvore. Alterar a coleção durante a iteração pode pular itens.

remover = [
    produto
    for produto in raiz.findall("produto")
    if produto.get("inativo") == "sim"
]
for produto in remover:
    raiz.remove(produto)

Parsing incremental com iterparse

iterparse() permite processar documentos grandes sem manter toda a árvore útil na memória. Ele ainda realiza leituras bloqueantes.

for evento, elemento in ET.iterparse(
    "dados.xml",
    events=("end",),
):
    if elemento.tag == "registro":
        processar(elemento)
        elemento.clear()

Use eventos end quando precisar de filhos e texto completos. No evento start, o conteúdo ainda pode não estar disponível. Chamar clear() depois de processar libera referências e reduz memória.

XMLPullParser para entrada em blocos

parser = ET.XMLPullParser(events=("end",))

for bloco in fonte_de_blocos():
    parser.feed(bloco)
    for evento, elemento in parser.read_events():
        if elemento.tag == "registro":
            processar(elemento)
            elemento.clear()

parser.close()

XMLPullParser é apropriado quando outra camada controla a leitura não bloqueante. Ainda é necessário impor limite total, quantidade de eventos e profundidade.

Limites de recursos

Antes do parsing, limite bytes. Durante o processamento, conte elementos, atributos, profundidade, tamanho de texto e número de namespaces. Interrompa ao ultrapassar o contrato.

MAX_ELEMENTOS = 100_000
contador = 0

for evento, elemento in ET.iterparse("dados.xml", events=("end",)):
    contador += 1
    if contador > MAX_ELEMENTOS:
        raise ValueError("XML excede o limite de elementos")

XML não confiável

XML pode explorar expansão de entidades, referências externas, profundidade excessiva e payloads gigantes, dependendo do parser e da versão. Para documentos recebidos de terceiros, considere uma biblioteca endurecida, isolamento e limites na infraestrutura.

Não confunda parsing bem-sucedido com validação. ElementTree não verifica automaticamente um XSD nem garante que a estrutura corresponda ao domínio.

XInclude

xml.etree.ElementInclude pode substituir elementos XInclude por arquivos ou recursos carregados. O loader padrão lê do disco. Não execute XInclude em XML fornecido pelo usuário sem loader restrito, raiz permitida e profundidade limitada.

from xml.etree import ElementInclude

ElementInclude.include(
    raiz,
    loader=loader_controlado,
    max_depth=3,
)

Nunca passe max_depth=None para conteúdo não confiável. Bloqueie URLs, caminhos absolutos e traversal.

Canonicalização

canonicalize() aplica C14N 2.0 para uma representação determinística útil em comparação e assinaturas.

normalizado = ET.canonicalize(
    xml_data=xml_original,
    with_comments=False,
    strip_text=False,
)

Canonicalização não valida assinatura nem torna conteúdo confiável. Ela apenas normaliza detalhes como namespaces, atributos e espaços conforme as opções.

Ordem de atributos

XML não atribui significado semântico à ordem de atributos. ElementTree preserva a ordem de criação em versões atuais, mas código de negócio não deve depender dela. Para saída criptográfica ou testes byte a byte, use canonicalização.

Encoding

tostring(..., encoding="unicode") retorna str; outros encodings retornam bytes. Combine corretamente com arquivos de texto ou binários. Não escreva str em stream binário nem bytes em stream de texto.

Para problemas de encoding, consulte codecs no Python.

Erros de parsing

XML malformado lança ET.ParseError, que possui posição e código em muitos casos.

try:
    raiz = ET.fromstring(xml_recebido)
except ET.ParseError as erro:
    linha, coluna = erro.position
    raise ValueError(
        f"XML inválido na linha {linha}, coluna {coluna}"
    ) from erro

Não devolva trechos sensíveis do documento em mensagens públicas.

Validação de domínio

Depois do parsing, valide tags obrigatórias, tipos, intervalos, cardinalidade e relações. Use Decimal para valores financeiros, datas com timezone definido e allowlists para enums e URLs.

Testes recomendados

Teste documento vazio, XML malformado, namespaces, atributos ausentes, texto em tail, XPath suportado e não suportado, arquivo grande, profundidade excessiva, muitos elementos, encoding divergente, escrita atômica, XInclude bloqueado e canonicalização.

Erros comuns

Os erros frequentes são carregar XML ilimitado, ignorar namespaces, assumir que .text contém todo o conteúdo, testar elemento com if not elemento, alterar filhos durante iteração, esperar XPath completo, habilitar XInclude livremente, depender da ordem de atributos e considerar parsing como validação.

Conclusão

ElementTree é uma solução prática para XML na biblioteca padrão. Use parse() e fromstring() para documentos pequenos, iterparse() para arquivos grandes e XMLPullParser para alimentação incremental. Modele namespaces explicitamente, valide o domínio e imponha limites antes e durante o parsing.

Consulte a documentação oficial de ElementTree e as orientações de segurança XML do Python. Para XML externo, combine parser atualizado, limites, validação e isolamento.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    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
    Código Python em tela representando inspeção de módulos e pacotes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifique pacotes Python

    Aprenda inspect.ispackage no Python para identificar pacotes, explorar módulos e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026