A biblioteca padrão do Python inclui um sólido parser XML — sem necessidade de pip install.
xml.etree.ElementTree lida com a grande maioria do XML do mundo real:
feeds RSS,
respostas SOAP, arquivos de configuração, arquivos de recursos Android, Maven POMs. Você só precisa
recorrer ao lxml
quando atingir validação de esquema XSD,
XPath complexo, ou arquivos realmente massivos. Vamos passar pelos dois, com exemplos reais.
Fundamentos do ElementTree — Analisando de uma String ou Arquivo
O módulo
xml.etree.ElementTree
oferece dois pontos de entrada: fromstring() para analisar strings XML, e
parse() para ler diretamente de um arquivo. Aqui está um exemplo prático usando
uma estrutura de feed RSS:
import xml.etree.ElementTree as ET
rss_xml = """<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Engineering Blog</title>
<link>https://blog.example.com</link>
<description>Articles for developers</description>
<item>
<title>Understanding Database Indexes</title>
<link>https://blog.example.com/db-indexes</link>
<pubDate>Mon, 15 Jan 2024 09:00:00 GMT</pubDate>
<category>Database</category>
</item>
<item>
<title>REST API Design Patterns</title>
<link>https://blog.example.com/rest-patterns</link>
<pubDate>Wed, 17 Jan 2024 09:00:00 GMT</pubDate>
<category>API</category>
</item>
</channel>
</rss>"""
# Analisa de uma string
root = ET.fromstring(rss_xml)
# Analisa de um arquivo (alternativa)
# tree = ET.parse('feed.xml')
# root = tree.getroot()
print(root.tag) # rss
print(root.attrib) # {'version': '2.0'}
channel = root.find('channel')
print(channel.find('title').text) # Engineering Blogfind, findall e findtext — Pesquisando na Árvore
Esses três métodos são suas ferramentas principais para extrair dados. Todos aceitam uma expressão de caminho simples (como um XPath limitado) para navegar pela árvore de elementos:
import xml.etree.ElementTree as ET
root = ET.fromstring(rss_xml)
channel = root.find('channel')
# find() — retorna o primeiro elemento correspondente, ou None
first_item = channel.find('item')
print(first_item.find('title').text) # Understanding Database Indexes
# findall() — retorna uma lista de todos os elementos correspondentes
items = channel.findall('item')
print(len(items)) # 2
for item in items:
title = item.findtext('title') # findtext() retorna .text diretamente
link = item.findtext('link')
pub_date = item.findtext('pubDate')
print(f"{title} — {pub_date}")
# Caminho aninhado com '/'
all_titles = channel.findall('item/title')
print([el.text for el in all_titles])
# ['Understanding Database Indexes', 'REST API Design Patterns']
# findtext() com um padrão (evita AttributeError em elementos ausentes)
author = channel.findtext('item/author', default='Unknown Author')
print(author) # Unknown Authorfindtext() com um padrão. Se você usar find().text
e o elemento não existir, find() retorna None e .text
lança um AttributeError. findtext('tag', default='') lida com
elementos ausentes graciosamente — muito mais seguro ao analisar XML de fontes externas.Lendo Atributos
import xml.etree.ElementTree as ET
xml_str = """<catalog>
<product id="P001" featured="true">
<name>Mechanical Keyboard</name>
<price currency="USD">189.00</price>
</product>
<product id="P002" featured="false">
<name>USB-C Hub</name>
<price currency="USD">49.99</price>
</product>
</catalog>"""
root = ET.fromstring(xml_str)
for product in root.findall('product'):
product_id = product.get('id') # get() para atributos
featured = product.get('featured', 'false') # com padrão
name = product.findtext('name')
price_el = product.find('price')
price = float(price_el.text)
currency = price_el.get('currency')
print(f"{product_id}: {name} — {currency} {price} (featured: {featured})")Lidando com Namespaces XML
Namespaces
são a parte da análise XML que faz a maioria dos desenvolvedores reclamar. No ElementTree,
URIs de namespace aparecem entre chaves nos nomes de tag: {http://...}tagname. Veja como
lidar com isso de forma limpa:
import xml.etree.ElementTree as ET
soap_xml = """<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:inv="http://www.example.com/invoice">
<soap:Body>
<inv:GetInvoiceResponse>
<inv:InvoiceId>INV-2024-0042</inv:InvoiceId>
<inv:Amount currency="EUR">1250.00</inv:Amount>
<inv:Status>Paid</inv:Status>
</inv:GetInvoiceResponse>
</soap:Body>
</soap:Envelope>"""
root = ET.fromstring(soap_xml)
# ElementTree expande prefixos de namespace para URIs entre chaves
# Você pode definir um mapa de namespace para buscas no estilo XPath mais limpas
ns = {
'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
'inv': 'http://www.example.com/invoice'
}
# Usa o mapa de namespace em find/findall
invoice_id = root.find('.//inv:InvoiceId', ns).text
amount_el = root.find('.//inv:Amount', ns)
status = root.findtext('.//inv:Status', namespaces=ns)
print(invoice_id) # INV-2024-0042
print(amount_el.text) # 1250.00
print(amount_el.get('currency')) # EUR
print(status) # PaidConstruindo XML Programaticamente
ElementTree também permite construir XML do zero — útil quando você precisa criar requisições SOAP ou gerar saída XML:
import xml.etree.ElementTree as ET
# Constrói um documento de pedido
order = ET.Element('order', id='ORD-9981', status='pending')
customer = ET.SubElement(order, 'customer')
ET.SubElement(customer, 'name').text = 'Jane Smith'
ET.SubElement(customer, 'email').text = '[email protected]'
items = ET.SubElement(order, 'items')
for product_id, name, qty, price in [
('P001', 'Mechanical Keyboard', 1, 189.00),
('P002', 'USB-C Hub', 2, 49.99),
]:
item = ET.SubElement(items, 'item', productId=product_id, qty=str(qty))
ET.SubElement(item, 'name').text = name
ET.SubElement(item, 'price', currency='USD').text = str(price)
# Serializa para string
ET.indent(order, space=' ') # Python 3.9+ — formata no lugar
xml_output = ET.tostring(order, encoding='unicode', xml_declaration=True)
print(xml_output)iterparse — Transmitindo Arquivos XML Grandes
Para arquivos XML grandes (dezenas de MB ou mais), carregar o documento inteiro na memória com
parse() é custoso.
iterparse()
transmite o arquivo (semelhante à abordagem SAX baseada em eventos) e dispara eventos à medida que os elementos são
encontrados, permitindo que você processe e descarte elementos conforme avança:
import xml.etree.ElementTree as ET
def process_large_feed(filepath):
"""Processa um feed RSS/Atom grande sem carregá-lo todo na memória."""
articles = []
for event, elem in ET.iterparse(filepath, events=('end',)):
if elem.tag == 'item':
articles.append({
'title': elem.findtext('title', ''),
'link': elem.findtext('link', ''),
'pub_date': elem.findtext('pubDate', ''),
})
# Essencial: limpa o elemento após processar para liberar memória
elem.clear()
if len(articles) >= 1000:
yield from articles
articles.clear()
yield from articles # retorna quaisquer itens restantes
for article in process_large_feed('large_feed.xml'):
print(article['title'])A chamada elem.clear() após processar cada elemento é a chave para manter
o uso de memória constante independentemente do tamanho do arquivo. Sem isso, ElementTree acumula todos os elementos
analisados na memória e você perde o benefício do streaming.
lxml — Quando Você Precisa de Mais Poder
A biblioteca
lxml
é uma biblioteca XML rápida baseada em C que estende a API do ElementTree com suporte completo a
XPath 1.0,
validação de esquema XSD e transformações XSLT. Instale com pip install lxml:
from lxml import etree
# lxml usa a mesma API que ElementTree na maioria dos casos
root = etree.fromstring(rss_xml.encode()) # lxml precisa de bytes, não str
# XPath 1.0 completo — muito mais poderoso que o subconjunto do ElementTree
items = root.xpath('//item[position() <= 2]/title/text()')
print(items) # ['Understanding Database Indexes', 'REST API Design Patterns']
# XPath com predicados — obtém itens de uma categoria específica
db_items = root.xpath('//item[category="Database"]/title/text()')
print(db_items) # ['Understanding Database Indexes']
# Validação de esquema XSD
xsd_doc = etree.parse('schema.xsd')
schema = etree.XMLSchema(xsd_doc)
xml_doc = etree.parse('data.xml')
if schema.validate(xml_doc):
print("Valid!")
else:
for error in schema.error_log:
print(f"Line {error.line}: {error.message}")Ferramentas Úteis para Trabalho com XML
Ao construir integrações XML em Python, essas ferramentas do navegador ajudam no lado dos dados: Formatador XML para tornar legíveis as respostas brutas de API, Validador XML para verificar o formato antes de conectar seu parser, e XML para JSON quando você preferir trabalhar com dicts em vez de elementos.
Conclusão
O xml.etree.ElementTree do Python cobre a maioria dos cenários XML do mundo real:
use find() e findall() com um mapa de namespace para SOAP e feeds com namespace,
findtext() com padrões para evitar AttributeError em elementos ausentes,
e iterparse() para arquivos grandes que você não pode carregar na memória de uma vez.
Recorra ao lxml quando precisar de validação XSD ou da linguagem de expressão XPath completa.
A biblioteca padrão lida bem com todo o resto.