Parsing XML and HTML Data with Python ElementTree

Parsing XML

XML Tree Structure and Elements

XML is a hierarchical data format with inherited structure, best represented as a tree. The xml.etree.ElementTree module provides two key classes: ElementTree represents an entire XML document as a tree, while Element represents individual nodes within that tree. File-level read/write operations typically occur at the ElementTree level, whereas interactions with individual XML elements and their children happen at the Element level.

The following XML document serves as our example data through out this section:

You can load this data by reading from a file:

The element tag, attribute names, and attribute values can be either
bytes or strings.

*tag* is the element name. *attrib* is an optional dictionary containing
element attributes. *extra* are additional element attributes given as
keyword arguments.

Example form:
    <tag attrib>text<child/>...</tag>tail

"""

# The element's name
tag = None

# Dictionary of the element's attributes
attrib = None

# Text before first subelement. Either a string or None
text = None

# Text after this element's end tag, before next sibling's start tag
tail = None

def __init__(self, tag, attrib={}, **extra):
    if not isinstance(attrib, dict):
        raise TypeError("attrib must be dict, not %s" % (
            attrib.__class__.__name__,))
    attrib = attrib.copy()
    attrib.update(extra)
    self.tag = tag
    self.attrib = attrib
    self._children = []

def makeelement(self, tag, attrib):
    """Create a new element with the same type."""
    return self.__class__(tag, attrib)

def copy(self):
    """Return a shallow copy of current element."""
    elem = self.makeelement(self.tag, self.attrib)
    elem.text = self.text
    elem.tail = self.tail
    elem[:] = self
    return elem

def __len__(self):
    return len(self._children)

def __getitem__(self, index):
    return self._children[index]

def __setitem__(self, index, element):
    self._children[index] = element

def __delitem__(self, index):
    del self._children[index]

def append(self, subelement):
    """Add *subelement* to the end of this element."""
    self._assert_is_element(subelement)
    self._children.append(subelement)

def extend(self, elements):
    """Append multiple subelements from a sequence."""
    for element in elements:
        self._assert_is_element(element)
    self._children.extend(elements)

def insert(self, index, subelement):
    """Insert *subelement* at position *index*."""
    self._assert_is_element(subelement)
    self._children.insert(index, subelement)

def remove(self, subelement):
    """Remove matching subelement by identity."""
    self._children.remove(subelement)

def find(self, path, namespaces=None):
    """Find first matching element by tag name or path."""
    return ElementPath.find(self, path, namespaces)

def findtext(self, path, default=None, namespaces=None):
    """Find text for first matching element."""
    return ElementPath.findtext(self, path, default, namespaces)

def findall(self, path, namespaces=None):
    """Find all matching subelements by tag name or path."""
    return ElementPath.findall(self, path, namespaces)

def iterfind(self, path, namespaces=None):
    """Find all matching subelements and return an iterator."""
    return ElementPath.iterfind(self, path, namespaces)

def clear(self):
    """Reset element - removes all subelements and attributes."""
    self.attrib.clear()
    self._children = []
    self.text = self.tail = None

def get(self, key, default=None):
    """Get element attribute value."""
    return self.attrib.get(key, default)

def set(self, key, value):
    """Set element attribute."""
    self.attrib[key] = value

def keys(self):
    """Get list of attribute names."""
    return self.attrib.keys()

def items(self):
    """Get element attributes as (name, value) tuples."""
    return self.attrib.items()

def iter(self, tag=None):
    """Create tree iterator yielding all matching elements."""
    if tag == "*":
        tag = None
    if tag is None or self.tag == tag:
        yield self
    for e in self._children:
        yield from e.iter(tag)

def itertext(self):
    """Create text iterator yielding all inner text."""
    tag = self.tag
    if not isinstance(tag, str) and tag is not None:
        return
    if self.text:
        yield self.text
    for e in self:
        yield from e.itertext()
        if e.tail:
            yield e.tail

</div>Since every node has access to these methods, and the parsing process provides us with the root node, we can leverage these methods to manipulate XML files.

**a. Iterating Through All XML Content**

<div>```
from xml.etree import ElementTree as ET

# Parse XML file directly
tree = ET.parse("country_data.xml")

# Get the root node
root = tree.getroot()

# Root tag
print(root.tag)

# Iterate through second level
for child in root:
    print(child.tag, child.attrib)
    # Iterate through third level
    for sub in child:
        print(sub.tag, sub.text)

Parse XML file

tree = ET.parse("country_data.xml")

Get the root node

root = tree.getroot()

Root tag

print(root.tag)

Find all 'year' nodes

for node in root.iter('year'): print(node.tag, node.text)


</div>**c. Modifying Node Content**

Modifications occur in memory and don't affect the original file. To persist changes, you must write the modified content back to a file.

Method 1: Parse string, modify, save

<div>```
from xml.etree import ElementTree as ET

# Parse from string
str_xml = open('country_data.xml', 'r').read()
root = ET.XML(str_xml)

# Operations
print(root.tag)

# Loop through all year nodes
for node in root.iter('year'):
    new_year = int(node.text) + 1
    node.text = str(new_year)

    # Set attributes
    node.set('name', 'alex')
    node.set('age', '18')
    # Delete attribute
    del node.attrib['name']

# Save file
tree = ET.ElementTree(root)
tree.write("modified.xml", encoding='utf-8')

Parse file directly

tree = ET.parse("country_data.xml") root = tree.getroot()

Operations

print(root.tag)

Loop through all year nodes

for node in root.iter('year'): new_year = int(node.text) + 1 node.text = str(new_year)

# Set attributes
node.set('name', 'alex')
node.set('age', '18')
# Delete attribute
del node.attrib['name']

Save file

tree.write("modified.xml", encoding='utf-8')


</div>**d. Deleting Nodes**

Method 1: Parse string, delete, save

<div>```
from xml.etree import ElementTree as ET

# Parse from string
str_xml = open('country_data.xml', 'r').read()
root = ET.XML(str_xml)

# Operations
print(root.tag)

# Iterate through all country nodes
for country in root.findall('country'):
    rank = int(country.find('rank').text)

    if rank > 50:
        root.remove(country)

# Save file
tree = ET.ElementTree(root)
tree.write("filtered.xml", encoding='utf-8')

Parse file

tree = ET.parse("country_data.xml") root = tree.getroot()

Operations

print(root.tag)

Iterate through all country nodes

for country in root.findall('country'): rank = int(country.find('rank').text)

if rank > 50:
    root.remove(country)

Save file

tree.write("filtered.xml", encoding='utf-8')


</div>#### Creating XML Documents

Method 1: Using Element constrcutor

<div>```
from xml.etree import ElementTree as ET

# Create root node
root = ET.Element("family")

# Create first child
son1 = ET.Element('son', {'name': 'child1'})
# Create second child
son2 = ET.Element('son', {"name": "child2"})

# Create grandchildren for first child
grandson1 = ET.Element('grandson', {'name': 'child11'})
grandson2 = ET.Element('grandson', {'name': 'child12'})
son1.append(grandson1)
son1.append(grandson2)

# Add children to root
root.append(son1)
root.append(son2)

tree = ET.ElementTree(root)
tree.write('output.xml', encoding='utf-8', short_empty_elements=False)

Create root node

root = ET.Element("family")

Create children using makeelement

son1 = root.makeelement('son', {'name': 'child1'}) son2 = root.makeelement('son', {"name": 'child2'})

Create grandchildren

grandson1 = son1.makeelement('grandson', {'name': 'child11'}) grandson2 = son1.makeelement('grandson', {'name': 'child12'})

son1.append(grandson1) son1.append(grandson2)

Add children to root

root.append(son1) root.append(son2)

tree = ET.ElementTree(root) tree.write('output.xml', encoding='utf-8', short_empty_elements=False)


</div>Method 3: Using SubElement

<div>```
from xml.etree import ElementTree as ET

# Create root node
root = ET.Element("family")

# Create children using SubElement
son1 = ET.SubElement(root, "son", attrib={'name': 'child1'})
son2 = ET.SubElement(root, "son", attrib={"name": "child2"})

# Create grandchild for first child
grandson1 = ET.SubElement(son1, "age", attrib={'name': 'child11'})
grandson1.text = 'value'

tree = ET.ElementTree(root)
tree.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)

def prettify(elem): """Convert element to string with indentation.""" rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent="\t")

Create root node

root = ET.Element("family")

Create children

son1 = root.makeelement('son', {'name': 'child1'}) son2 = root.makeelement('son', {"name": 'child2'})

Create grandchildren

grandson1 = son1.makeelement('grandson', {'name': 'child11'}) grandson2 = son1.makeelement('grandson', {'name': 'child12'})

son1.append(grandson1) son1.append(grandson2)

Add children to root

root.append(son1) root.append(son2)

Write with formatting

raw_str = prettify(root) f = open("formatted.xml", 'w', encoding='utf-8') f.write(raw_str) f.close()


</div>

Tags: python XML Parsing ElementTree Data Processing XML manipulation

Posted on Mon, 21 Sep 2026 16:33:56 +0000 by coellen