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.







