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

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

    poplib no Python: leia e-mails com POP3

    Aprenda poplib no Python para acessar POP3 com TLS, listar e baixar mensagens, usar UIDL, limitar dados e evitar exclusões

    Ler mais

    Tempo de leitura: 6 minutos
    22/08/2026
    A close-up of a laptop on a table, displaying a book on test-driven software with Python, set in a comfortable environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    imaplib no Python: leia e-mails com IMAP

    Aprenda imaplib no Python para acessar caixas IMAP com TLS, buscar por UID, ler mensagens sem marcá-las, usar flags e

    Ler mais

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

    ftplib no Python: FTP e FTPS seguros

    Aprenda ftplib no Python para listar, baixar e enviar arquivos por FTP ou FTPS com TLS, timeouts, limites, retomada e

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Rack de servidores representando um endpoint criado com xmlrpc.server no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crie servidores XML-RPC

    Aprenda xmlrpc.server no Python para criar servidores XML-RPC, registrar funções, limitar métodos e caminhos e evitar exposição insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Cabos conectados a servidor representando chamadas remotas com xmlrpc.client no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.client no Python: chamadas RPC

    Aprenda xmlrpc.client no Python para chamar serviços XML-RPC, tratar Fault e ProtocolError, usar TLS, tipos compatíveis e limites seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    21/08/2026
    Código de erro sobre dados binários representando falhas tratadas com urllib.error no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error no Python: trate falhas HTTP

    Aprenda urllib.error no Python para tratar URLError, HTTPError, downloads incompletos, retries seletivos e diagnósticos de rede mais claros.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026