XML Parsing in Python — Complete Guide with ElementTree and lxml
In this tutorial, you'll learn about XML Parsing in Python. We cover key concepts, practical examples, and best practices.
XML parsing in Python lets you read, extract, modify, and create XML documents using libraries like ElementTree (built-in) and lxml, enabling everything from configuration file processing to web service data handling.
What You'll Learn
- Parsing XML with Python's built-in ElementTree module
- Using lxml for advanced parsing with XPath support
- SAX and DOM parsing approaches for different use cases
- Creating and modifying XML documents programmatically
Why XML Parsing in Python Matters
Python is one of the most common languages for processing XML data — from parsing RSS feeds and configuration files to handling SOAP responses and XML-based data interchange. Durga Antivirus Pro uses Python with lxml to parse XML threat signature files, extracting malware patterns and validating updates before applying them to the scan engine.
Learning Path
flowchart LR A[XML Basics] --> B[XPath Queries] B --> C[XML Parsing Python
You are here] C --> D[XML Parsing Java] D --> E[XML Parsing JavaScript]
Setup and Sample Data
No external installation needed for ElementTree — it's built into Python. For lxml, install separately:
pip install lxml
Sample XML file used throughout this guide:
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book category="fiction" id="b1">
<title>The Hobbit</title>
<author>J.R.R. Tolkien</author>
<year>1937</year>
<price currency="USD">12.99</price>
</book>
<book category="non-fiction" id="b2">
<title>A Brief History of Time</title>
<author>Stephen Hawking</author>
<year>1988</year>
<price currency="GBP">9.99</price>
</book>
<book category="fiction" id="b3">
<title>1984</title>
<author>George Orwell</author>
<year>1949</year>
<price currency="USD">10.99</price>
</book>
</library>
Parsing with ElementTree
ElementTree is Python's standard XML parser:
import xml.etree.ElementTree as ET
# Parse from file
tree = ET.parse('library.xml')
root = tree.getroot()
# Root tag and attributes
print(f"Root: {root.tag}")
print(f"Number of books: {len(root.findall('book'))}")
# Iterate through books
for book in root.findall('book'):
title = book.find('title').text
author = book.find('author').text
year = book.find('year').text
price = book.find('price').text
category = book.get('category')
print(f"'{title}' by {author} ({year}) - ${price} [{category}]")
# Expected output:
# Root: library
# Number of books: 3
# 'The Hobbit' by J.R.R. Tolkien (1937) - $12.99 [fiction]
# 'A Brief History of Time' by Stephen Hawking (1988) - $9.99 [non-fiction]
# '1984' by George Orwell (1949) - $10.99 [fiction]
Modifying XML with ElementTree
You can modify existing XML and write it back:
import xml.etree.ElementTree as ET
tree = ET.parse('library.xml')
root = tree.getroot()
# Add a new book
new_book = ET.SubElement(root, 'book')
new_book.set('category', 'sci-fi')
new_book.set('id', 'b4')
ET.SubElement(new_book, 'title').text = 'Dune'
ET.SubElement(new_book, 'author').text = 'Frank Herbert'
ET.SubElement(new_book, 'year').text = '1965'
price_elem = ET.SubElement(new_book, 'price')
price_elem.text = '14.99'
price_elem.set('currency', 'USD')
# Update price of first book
first_book = root.find('book')
first_book.find('price').text = '13.99'
# Save to new file
tree.write('library_updated.xml', encoding='UTF-8', xml_declaration=True)
print("Updated library saved.")
# Verify
for book in root.findall('book'):
print(f"{book.find('title').text}: ${book.find('price').text}")
# Expected output:
# Updated library saved.
# The Hobbit: $13.99
# A Brief History of Time: $9.99
# 1984: $10.99
# Dune: $14.99
Advanced Parsing with lxml
lxml provides full XPath support and better performance:
from lxml import etree
# Parse with lxml
tree = etree.parse('library.xml')
# Full XPath support
fiction_titles = tree.xpath("//book["@category"='fiction']/title/text()")
print("Fiction titles:", fiction_titles)
# Find books under $11
budget_books = tree.xpath("//book[price < 11]")
for book in budget_books:
title = book.xpath("title/text()")[0]
price = book.xpath("price/text()")[0]
print(f"Budget book: {title} - ${price}")
# Complex query with axes
results = tree.xpath("//book[1]/following-sibling::book/title/text()")
print("Books after first:", results)
# Expected output:
# Fiction titles: ['The Hobbit', '1984']
# Budget book: A Brief History of Time - $9.99
# Budget book: 1984 - $10.99
# Books after first: ['A Brief History of Time', '1984']
Creating XML from Scratch
ElementTree can build XML documents from scratch:
import xml.etree.ElementTree as ET
# Create root
library = ET.Element('library')
# Add books
books_data = [
{'title': 'The Hobbit', 'author': 'J.R.R. Tolkien', 'year': '1937',
'price': '12.99', 'cat': 'fiction'},
{'title': '1984', 'author': 'George Orwell', 'year': '1949',
'price': '10.99', 'cat': 'fiction'},
]
for data in books_data:
book = ET.SubElement(library, 'book')
book.set('category', data['cat'])
ET.SubElement(book, 'title').text = data['title']
ET.SubElement(book, 'author').text = data['author']
ET.SubElement(book, 'year').text = data['year']
ET.SubElement(book, 'price').text = data['price']
# Create tree and write
tree = ET.ElementTree(library)
tree.write('output.xml', encoding='UTF-8', xml_declaration=True)
# Pretty-print (Python 3.9+)
ET.indent(tree, space=' ')
tree.write('output_pretty.xml', encoding='UTF-8', xml_declaration=True)
print("XML files created successfully.")
# Verify output
with open('output_pretty.xml', 'r') as f:
print(f.read())
# Expected output:
# XML files created successfully.
# <?xml version='1.0' encoding='UTF-8'?>
# <library>
# <book category="fiction">
# <title>The Hobbit</title>
# <author>J.R.R. Tolkien</author>
# <year>1937</year>
# <price>12.99</price>
# </book>
# <book category="fiction">
# <title>1984</title>
# <author>George Orwell</author>
# <year>1949</year>
# <price>10.99</price>
# </book>
# </library>
SAX Parsing for Large Files
SAX is event-driven — it reads large XML files without loading the entire tree into memory:
import xml.sax
class BookHandler(xml.sax.ContentHandler):
def __init__(self):
self.current_tag = ""
self.current_book = {}
self.in_book = False
def startElement(self, tag, attrs):
self.current_tag = tag
if tag == 'book':
self.in_book = True
self.current_book = {'category': attrs.get('category', ''),
'id': attrs.get('id', '')}
def characters(self, content):
if self.in_book and self.current_tag in ('title', 'author', 'year', 'price'):
self.current_book[self.current_tag] = content.strip()
def endElement(self, tag):
if tag == 'book':
print(f"Book: {self.current_book.get('title', 'N/A')} "
f"by {self.current_book.get('author', 'N/A')}")
self.in_book = False
# Parse with SAX
parser = xml.sax.make_parser()
handler = BookHandler()
parser.setContentHandler(handler)
# This uses very little memory even with huge files
parser.parse('library.xml')
# Expected output:
# Book: The Hobbit by J.R.R. Tolkien
# Book: A Brief History of Time by Stephen Hawking
# Book: 1984 by George Orwell
Security Angle
XML parsing in Python is vulnerable to XXE (XML External Entity) attacks if not properly configured. Python's ElementTree is safe by default in Python 3.7.1+, but lxml requires explicit configuration:
from lxml import etree
# Secure parser: disable entities and DTD
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
dtd_validation=False,
load_dtd=False
)
# Attempt to parse a potentially malicious file
try:
tree = etree.parse('untrusted.xml', parser)
print("Parsed safely with entities disabled.")
except etree.XMLSyntaxError as e:
print(f"Parse error (XXE blocked): {e}")
# Expected output:
# Parsed safely with entities disabled.
# (If the XML contains XXE, the parser silently ignores entity references)
Durga Antivirus Pro uses secure parsers with all entity resolution disabled when scanning untrusted XML files from email attachments and downloaded archives.
Common Mistakes
1. Forgetting to strip whitespace from text content
elem.text includes surrounding whitespace. Always call .strip() on extracted text.
2. Using find instead of findall
find returns the first match. findall returns all matches. Confusing them leads to missing data.
3. Not handling namespaces
XML with namespaces requires the full {uri}tag syntax in ElementTree. Use lxml with xpath for simpler namespace handling.
4. Loading entire large files into memory
For files over 100MB, use SAX or iterparse to process incrementally instead of loading the full tree.
Practice Questions
What is the difference between ElementTree and lxml? ElementTree is built-in but has limited XPath support. lxml is faster, supports full XPath 1.0, and has better namespace handling.
When should you use SAX parsing instead of DOM? SAX is event-driven and memory-efficient for large files. Use SAX when the file is too big to fit in memory as a tree.
How do you prevent XXE attacks in Python XML parsing? Disable entity resolution and DTD loading. Use the secure parser configuration in lxml or stick with ElementTree (safe in Python 3.7.1+).
Challenge: Write a Python script that reads an RSS feed XML, extracts the latest 5 articles, and outputs them as a formatted HTML snippet with clickable links and publication dates.
FAQ
Try It Yourself
Run this complete example that parses XML from a string:
import xml.etree.ElementTree as ET
xml_string = """<?xml version="1.0"?>
<books>
<book><title>Learning Python</title><price>39.99</price></book>
<book><title>XML Essentials</title><price>29.99</price></book>
</books>"""
root = ET.fromstring(xml_string)
total = 0
for book in root.findall('book'):
title = book.find('title').text
price = float(book.find('price').text)
total += price
print(f"{title}: ${price:.2f}")
print(f"Total: ${total:.2f}")
# Expected output:
# Learning Python: $39.99
# XML Essentials: $29.99
# Total: $69.98
What's Next
| Tutorial | What You'll Learn |
|---|---|
| XML Basics — Complete Guide | Foundational XML concepts |
| XPath Explained — Querying XML | Navigate XML documents with path expressions |
| XSLT Explained — Transform XML | Transform XML into HTML and other formats |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-23.
What's Next
Congratulations on completing this XML Parsing in Python tutorial! Here's where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Create a script that processes RSS feeds using ElementTree
- Explore related topics — Check out other XML tutorials in this category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro