Pythons standardbibliotek levereras med en solid XML-tolk — ingen pip install krävs.
xml.etree.ElementTree hanterar den stora majoriteten av verklig XML:
RSS-flöden,
SOAP-svar, konfigurationsfiler, Android-resursfiler, Maven POM:ar. Du behöver bara
lxml
när du stöter på
XSD-schemavalidering,
komplex XPath eller riktigt stora filer. Låt oss gå igenom båda med verkliga exempel.
ElementTree-grunder — Tolka från sträng eller fil
Modulen
xml.etree.ElementTree
ger dig två ingångspunkter: fromstring() för att tolka XML-strängar och
parse() för att läsa direkt från en fil. Här är ett praktiskt exempel med
en RSS-flödesstruktur:
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>"""
# Tolka från en sträng
root = ET.fromstring(rss_xml)
# Tolka från en fil (alternativ)
# 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 och findtext — Söka i trädet
Dessa tre metoder är dina primära verktyg för att extrahera data. Alla accepterar ett enkelt sökvägsuttryck (som en begränsad XPath) för att navigera i elementträdet:
import xml.etree.ElementTree as ET
root = ET.fromstring(rss_xml)
channel = root.find('channel')
# find() — returnerar det första matchande elementet, eller None
first_item = channel.find('item')
print(first_item.find('title').text) # Understanding Database Indexes
# findall() — returnerar en lista med alla matchande element
items = channel.findall('item')
print(len(items)) # 2
for item in items:
title = item.findtext('title') # findtext() returnerar .text direkt
link = item.findtext('link')
pub_date = item.findtext('pubDate')
print(f"{title} — {pub_date}")
# Kapslad sökväg med '/'
all_titles = channel.findall('item/title')
print([el.text for el in all_titles])
# ['Understanding Database Indexes', 'REST API Design Patterns']
# findtext() med ett standardvärde (undviker AttributeError för saknade element)
author = channel.findtext('item/author', default='Unknown Author')
print(author) # Unknown Authorfindtext() med ett standardvärde. Om du använder find().text
och elementet inte finns returnerar find() None och .text
kastar ett AttributeError. findtext('tag', default='') hanterar saknade
element smidigt — mycket säkrare när du tolkar XML från externa källor.Läsa attribut
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() för attribut
featured = product.get('featured', 'false') # med standardvärde
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})")Hantera XML-namnrymder
Namnrymder
är den del av XML-tolkning som får de flesta utvecklare att stöna. I ElementTree
visas namnrymds-URI:er inom klammerparentes i taggnamn: {http://...}taggnamn. Så här
hanterar du dem på ett rent sätt:
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 expanderar namnrymdsprefix till URI:er i klammerparentes
# Du kan definiera en namnrymdskarta för renare XPath-liknande sökningar
ns = {
'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
'inv': 'http://www.example.com/invoice'
}
# Använd namnrymdskartan i 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) # PaidBygga XML programmatiskt
ElementTree låter dig också konstruera XML från grunden — användbart när du behöver bygga SOAP-förfrågningar eller generera XML-utdata:
import xml.etree.ElementTree as ET
# Bygg ett beställningsdokument
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)
# Serialisera till sträng
ET.indent(order, space=' ') # Python 3.9+ — snyggt formatera på plats
xml_output = ET.tostring(order, encoding='unicode', xml_declaration=True)
print(xml_output)iterparse — Strömma stora XML-filer
För stora XML-filer (tiotals MB eller mer) är det dyrt att läsa in hela dokumentet i minnet
med parse().
iterparse()
strömmar filen (liknande det händelsedrivna SAX-tillvägagångssättet) och utlöser händelser när element
påträffas, vilket låter dig bearbeta och kassera element allteftersom:
import xml.etree.ElementTree as ET
def process_large_feed(filepath):
"""Bearbeta ett stort RSS/Atom-flöde utan att läsa in allt i minnet."""
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', ''),
})
# Kritiskt: rensa elementet efter bearbetning för att frigöra minne
elem.clear()
if len(articles) >= 1000:
yield from articles
articles.clear()
yield from articles # yield eventuella återstående
for article in process_large_feed('large_feed.xml'):
print(article['title'])Anropet elem.clear() efter bearbetning av varje element är nyckeln till att
hålla minnesanvändningen konstant oavsett filstorlek. Utan det ackumulerar ElementTree alla tolkade
element i minnet och du förlorar fördelen med strömning.
lxml — När du behöver mer kraft
Biblioteket
lxml
är ett snabbt C-baserat XML-bibliotek som utökar ElementTree-API:t med fullt
XPath 1.0-stöd,
XSD-schemavalidering och XSLT-transformationer. Installera med pip install lxml:
from lxml import etree
# lxml använder samma API som ElementTree i de flesta fall
root = etree.fromstring(rss_xml.encode()) # lxml behöver bytes, inte str
# Fullt XPath 1.0 — mycket kraftfullare än ElementTree-delmängden
items = root.xpath('//item[position() <= 2]/title/text()')
print(items) # ['Understanding Database Indexes', 'REST API Design Patterns']
# XPath med predikat — hämta element från en specifik kategori
db_items = root.xpath('//item[category="Database"]/title/text()')
print(db_items) # ['Understanding Database Indexes']
# XSD-schemavalidering
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}")Användbara verktyg för XML-arbete
När du bygger Python XML-integrationer hjälper dessa webbläsarverktyg med datasidan: XML Formatter för att göra råa API-svar läsbara, XML Validator för att kontrollera välformning innan du kopplar upp din tolk, och XML to JSON när du hellre vill arbeta med dict:ar istället för element.
Sammanfattning
Pythons xml.etree.ElementTree täcker de flesta verkliga XML-scenarier:
använd find() och findall() med en namnrymdskarta för SOAP och
namnrymdsbaserade flöden, findtext() med standardvärden för att undvika AttributeError
vid saknade element, och iterparse() för stora filer du inte kan läsa in i minnet på en gång.
Nå efter lxml när du behöver XSD-validering eller det fullständiga XPath-uttrycksspråket.
Standardbiblioteket klarar allt annat utmärkt.