Pythons standardbibliotek leveres med en solid XML-parser — ingen pip install nødvendig.
xml.etree.ElementTree håndterer de aller fleste virkelige XML-tilfeller:
RSS-feeder,
SOAP-svar, konfigurasjonsfiler, Android-ressursfiler, Maven POM-er. Du trenger bare å
nå etter lxml
når du støter på XSD-skjemavalidering,
kompleks XPath eller virkelig store filer. La oss gå gjennom begge med virkelige eksempler.
ElementTree-grunnleggende — Parsing fra streng eller fil
Modulen
xml.etree.ElementTree
gir deg to inngangspunkter: fromstring() for parsing av XML-strenger og
parse() for direkte lesing fra en fil. Her er et praktisk eksempel med
en RSS-feed-struktur:
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>"""
# Parser fra en streng
root = ET.fromstring(rss_xml)
# Parser fra 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 og findtext — Søk i treet
Disse tre metodene er dine primære verktøy for å hente ut data. De aksepterer alle et enkelt stiuttrykk (som en begrenset XPath) for å navigere i elementtreet:
import xml.etree.ElementTree as ET
root = ET.fromstring(rss_xml)
channel = root.find('channel')
# find() — returnerer det første samsvarende elementet, eller None
first_item = channel.find('item')
print(first_item.find('title').text) # Understanding Database Indexes
# findall() — returnerer en liste over alle samsvarende elementer
items = channel.findall('item')
print(len(items)) # 2
for item in items:
title = item.findtext('title') # findtext() returnerer .text direkte
link = item.findtext('link')
pub_date = item.findtext('pubDate')
print(f"{title} — {pub_date}")
# Nestet sti med '/'
all_titles = channel.findall('item/title')
print([el.text for el in all_titles])
# ['Understanding Database Indexes', 'REST API Design Patterns']
# findtext() med en standardverdi (unngår AttributeError for manglende elementer)
author = channel.findtext('item/author', default='Unknown Author')
print(author) # Unknown Authorfindtext() med en standardverdi. Hvis du bruker find().text
og elementet ikke finnes, returnerer find() None og .text
kaster en AttributeError. findtext('tag', default='') håndterer manglende
elementer elegant — mye tryggere ved parsing av XML fra eksterne kilder.Lese attributter
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() for attributter
featured = product.get('featured', 'false') # med standardverdi
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})")Håndtering av XML-navnerom
Navnerom
er den delen av XML-parsing som får de fleste utviklere til å sukke. I ElementTree
vises navnerom-URI-er i krøllede parenteser i taggnavn: {http://...}taggnavn. Slik
håndterer du dem på en ryddig måte:
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 utvider navnerom-prefikser til URI-er i krøllede parenteser
# Du kan definere et navneromskart for renere XPath-lignende søk
ns = {
'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
'inv': 'http://www.example.com/invoice'
}
# Bruk navneroms kartet 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) # PaidBygge XML programmatisk
ElementTree lar deg også konstruere XML fra bunnen av — nyttig når du trenger å bygge SOAP-forespørsler eller generere XML-utdata:
import xml.etree.ElementTree as ET
# Bygg et bestillingsdokument
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)
# Serialiser til streng
ET.indent(order, space=' ') # Python 3.9+ — formatér på stedet
xml_output = ET.tostring(order, encoding='unicode', xml_declaration=True)
print(xml_output)iterparse — Strømming av store XML-filer
For store XML-filer (titalls MB eller mer) er det kostbart å laste inn hele dokumentet i minnet
med parse().
iterparse()
strømmer filen (lignende den hendelsesdrevne SAX-tilnærmingen) og utløser hendelser når elementer
oppdages, slik at du kan behandle og kaste elementer underveis:
import xml.etree.ElementTree as ET
def process_large_feed(filepath):
"""Behandle en stor RSS/Atom-feed uten å laste alt inn 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', ''),
})
# Kritisk: tøm elementet etter behandling for å frigjøre minne
elem.clear()
if len(articles) >= 1000:
yield from articles
articles.clear()
yield from articles # yield eventuelle gjenværende
for article in process_large_feed('large_feed.xml'):
print(article['title'])Kallet elem.clear() etter behandling av hvert element er nøkkelen til å holde
minnebruken konstant uavhengig av filstørrelse. Uten det akkumulerer ElementTree alle parsede elementer
i minnet og du mister fordelen med strømming.
lxml — Når du trenger mer kraft
Biblioteket
lxml
er et raskt C-basert XML-bibliotek som utvider ElementTree-API-et med full
XPath 1.0-støtte,
XSD-skjemavalidering og XSLT-transformasjoner. Installer med pip install lxml:
from lxml import etree
# lxml bruker den samme API-en som ElementTree i de fleste tilfeller
root = etree.fromstring(rss_xml.encode()) # lxml trenger bytes, ikke str
# Full XPath 1.0 — mye kraftigere enn ElementTree-delmengden
items = root.xpath('//item[position() <= 2]/title/text()')
print(items) # ['Understanding Database Indexes', 'REST API Design Patterns']
# XPath med predikater — hent elementer fra en bestemt kategori
db_items = root.xpath('//item[category="Database"]/title/text()')
print(db_items) # ['Understanding Database Indexes']
# XSD-skjemavalidering
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}")Nyttige verktøy for XML-arbeid
Når du bygger Python XML-integrasjoner, hjelper disse nettleserverktøyene med datasiden: XML Formatter for å gjøre rå API-svar lesbare, XML Validator for å sjekke velformethet før du kobler opp parseren din, og XML to JSON når du heller vil jobbe med dicts enn elementer.
Oppsummering
Pythons xml.etree.ElementTree dekker de fleste virkelige XML-scenarier:
bruk find() og findall() med et navneromskart for SOAP og
navneroms-feeder, findtext() med standardverdier for å unngå AttributeError
for manglende elementer, og iterparse() for store filer du ikke kan laste inn i minnet
på en gang. Nå etter lxml når du trenger XSD-validering eller det fullstendige
XPath-uttrykksspråket. Standardbiblioteket håndterer alt annet fint.