Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

X is for XML parsing - Python A to Z

Code in this blog post was written with versions: Python: 3.14, beautifulsoup4: 4.15.0, lxml: 6.1.2

Python A-Z is a blog series about Python. Each day, I share insights, ideas and examples for different parts of Python development that match with the letter of the day. Blaugust is an annual blogging festival in August where the goal is to write a blog post every day of the month.

Today I’m doing a bit of piggybacking: XML fits the “X” of the day but I’m also going to talk about HTML parsing because we can use the same tool. I’m also cheating a bit (honestly, I couldn’t come up with a new thing starting with X to write about) because I’ll mostly just point you towards my previous work. I’m doing this to bring new eyes to BeautifulSoup as my old blog posts don’t pop up so often in the feeds.

I’m starting with a story time:

It’s probably one of the things I’ve written and spoken more about than anything else: parsing these formats with BeautifulSoup. My first ever developer conference talk was in PyCon Finland 2016 where I came with two slides: opening and closing and a demo without a backup that was using a live website in the Internet. Looking back now, I’m slightly happy I didn’t know better because there were so many things I would now be super stressed out but didn’t realise back then. Luckily, the demo went well. I haven’t dared to rewatch it in years though.

BeautifulSoup describes itself by saying

Beautiful Soup is a Python library for pulling data out of HTML and XML files. It works with your favorite parser to provide idiomatic ways of navigating, searching, and modifying the parse tree. It commonly saves programmers hours or days of work.

I wrote about it six years ago and not a ton has changed to be honest.

One thing I didn’t write about six years ago that I use all the time now is select that allows directly querying with CSS selectors.

Parsing HTML

I’m borrowing the examples from documentation.

We have a simple HTML doc with a couple of paragraphs and links.

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

To parse that, we do

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc, 'html.parser')

We can then start querying into soup with select :

soup.select("title") # [<title>The Dormouse's story</title>]
soup.select("p > a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select('a[href$="tillie"]')
# [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

If you have done frontend development and are familiar with CSS selectors, this is a really nice way compared to traversing the tree manually step by step.

Parsing XML

The same selectors work with XML documents. To test it out, let’s take a look at my RSS feed (I redacted the actual content of the blog posts for brevity):

xml_doc = """<feed>
<title>
Juha-Matti Santala - Community Builder. Dreamer. Adventurer.
</title>
<subtitle/>
<link href="" rel="self"/>
<link href="https://hamatti.org"/>
<updated>2026-08-26T00:00:00Z</updated>
<id/>
<author>
<name>Juha-Matti Santala</name>
<email>juhis@hamatti.org</email>
</author>
<entry>
<title>W is for walrus operator - Python A to Z</title>
<link href="https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/"/>
<updated>2026-08-26T00:00:00Z</updated>
<id>
https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/
</id>
<content type="html">
[ redacted for brevity ]
</content>
</entry>
<entry>
<title>V is for visible characters - Python A to Z</title>
<link href="https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/"/>
<updated>2026-08-25T00:00:00Z</updated>
<id>
https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/
</id>
<content type="html">
[ redacted for brevity ]
</content>
</entry>"""

To explore the XML document with CSS selectors:

from bs4 import BeautifulSoup
# You also need lxml installed to project

soup = BeautifulSoup(xml_doc, "lxml")

soup.select('author')
# [<author>
# <name>Juha-Matti Santala</name>
# <email>juhis@hamatti.org</email>
# </author>]

soup.select('entry id')
# [
# <id>https://hamatti.org/posts/w-is-for-walrus-operator-python-a-to-z/ </id>, 
# <id>https://hamatti.org/posts/v-is-for-visible-characters-python-a-to-z/ </id>
# ]


If something above resonated with you, let's start a discussion about it! Email me at juhis@hamatti.org and share your thoughts. This year, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.