XML Tutorial · XML in Code · Lesson 17 of 28
XML in Python
Reading, building and writing XML in Python with the standard library's ElementTree and with lxml, and when to use which.
ElementTree or lxml?
xml.etree.ElementTree (standard library) |
lxml |
|
|---|---|---|
| Install | Built in | pip install lxml |
| XPath | A limited subset (findall("./item[@qty]")) |
Full XPath 1.0 |
| XSLT, XSD, RELAX NG, Schematron | No | Yes |
| Speed | Fast (C accelerator) | Faster, plus streaming tools |
Parent pointers (getparent()) |
No | Yes |
| API | ElementTree | ElementTree-compatible, plus extras |
Start with ElementTree for simple reading and writing; use lxml for XPath, validation, XSLT or big documents. For untrusted input, read XML security first.
Reading with ElementTree
import xml.etree.ElementTree as ET
root = ET.fromstring("""<shop>
<product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.50</price><stock>40</stock></product>
<product sku="COF-02" category="drinks"><name>Coffee beans</name><price>9.90</price><stock>0</stock></product>
<product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>12.00</price><stock>15</stock></product>
<product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>29.00</price><stock>4</stock></product>
</shop>""")
for p in root.findall("product"):
print(p.get("sku"), p.findtext("name"), float(p.findtext("price")))
print("in stock:", [p.findtext("name") for p in root.findall("product") if int(p.findtext("stock")) > 0])
print("kitchen:", [p.get("sku") for p in root.findall("product[@category='kitchen']")])
Output
TEA-01 Green tea 3.5 COF-02 Coffee beans 9.9 MUG-07 Camel mug 12.0 PAN-03 Frying pan 29.0 in stock: ['Green tea', 'Camel mug', 'Frying pan'] kitchen: ['MUG-07', 'PAN-03']
Building and writing XML
import xml.etree.ElementTree as ET
order = ET.Element("order", id="1042")
ET.SubElement(order, "customer").text = "Ada & Co"
for sku, qty in [("TEA-01", 2), ("MUG-07", 1)]:
ET.SubElement(order, "item", sku=sku, qty=str(qty))
ET.indent(order) # pretty-print (Python 3.9+)
print(ET.tostring(order, encoding="unicode"))
print(ET.tostring(order, xml_declaration=True, encoding="UTF-8")[:38])
Output
<order id="1042"> <customer>Ada & Co</customer> <item sku="TEA-01" qty="2" /> <item sku="MUG-07" qty="1" /> </order> b"<?xml version='1.0' encoding='UTF-8'?>"
Modifying a document
from lxml import etree
shop = etree.fromstring(b"""<shop>
<product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.50</price><stock>40</stock></product>
<product sku="COF-02" category="drinks"><name>Coffee beans</name><price>9.90</price><stock>0</stock></product>
<product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>12.00</price><stock>15</stock></product>
<product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>29.00</price><stock>4</stock></product>
</shop>""")
for p in shop.xpath("//product[stock = 0]"):
p.getparent().remove(p) # remove sold-out products
for price in shop.iter("price"):
price.text = f"{float(price.text) * 1.1:.2f}" # raise prices 10%
shop.set("updated", "2026-10-22")
print(etree.tostring(shop, pretty_print=True).decode())
Output
<shop updated="2026-10-22"> <product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.85</price><stock>40</stock></product> <product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>13.20</price><stock>15</stock></product> <product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>31.90</price><stock>4</stock></product> </shop>
Test yourself
Which Python library supports XSD validation and full XPath 1.0?
lxmlxml.etree.ElementTreejsoncsv
Show answer
Answer: lxml
ElementTree only supports a small XPath subset and no schemas; lxml adds both.