Concepts / Extracting Text and Attributes from XML

Extracting Text and Attributes from XML

findall() returns a Python list of all Element objects matching a given path in the XML tree.

  • Programming
Interactive lab

Try it: XML Element Trees

How ET.fromstring() turns XML text into a tree of Elements (tag, attributes, .text), and how find(), findall() and iter() walk that tree along a path such as users/user.

How it works

  1. The parser reads start tags, text and end tags left to right, keeping a stack of open elements; each start tag becomes an Element that is a child of the element on top of the stack.
  2. Text before an element's first child becomes its .text (whitespace included); an end tag must match the open element or parsing stops with ParseError.
  3. findall(path) starts at the element it is called on (the root is not part of the path) and follows each path segment down one level of children.
  4. find(path) returns the first match or None; iter(tag) visits every element in document order, including the root.
  5. Reading .text, .tag or .get('attr') on each result gives the extracted values; reading an attribute from None raises AttributeError.

Default run (29 steps): ET.fromstring() receives 183 characters. The parser reads tags left to right and keeps a stack of open elements. … findall() returned a list of 2 elements; reading e.get('x') from each gives ['2', '7'].

Simplified: A small, hand-written XML parser for a subset: elements, attributes, text, self-closing tags, comments, CDATA, processing instructions and the five predefined entities plus numeric character references. DOCTYPE/DTDs and namespaces are not supported. Error messages follow Python's expat parser for these cases (column numbers can differ for non-ASCII documents). Paths support tags, *, ., //, [@attr], [@attr='v'], [tag] and [n].

Educational simulation

Loading the simulation…

From One Record to Many

Real XML data commonly contains groups of related records rather than one isolated element. A users list may contain many user records, a product catalog may contain many products, and a weather feed may contain forecasts for multiple days. Processing this kind of data requires a way to collect every matching element and then examine the elements one at a time.

The findall() method is the collection step. It follows a path through the XML tree, finds every matching element, and returns those matches as a Python list. Each item in that list is a complete XML Element object, not merely a text value. This means every item can still be queried for child elements, attributes, and text.

Following the XML Path

Before using findall(), picture the hierarchy of the XML. In the teaching example, user elements are nested inside a users container. Each user contains child elements such as id and name, and also has an x attribute. The path users/user means: move into the users element, then collect the user elements found inside it.

users/userXML pathuserscontainer elementuserElement objectPython listmatched ElementsuserElement objectlocatescontainscontainsadded toadded to
How does an XML path select multiple matching nodes from the tree and turn them into a Python list of Element objects?
Worked example

Collecting User Elements

An XML hierarchy contains a users container with several user elements inside it. Determine what the path users/user is intended to collect.

  1. 1
    Read the path

    The path names users first and user second, so the search moves through the users container to its user elements.

  2. 2
    Collect every match

    findall() gathers every user element matching that path rather than stopping after the first one.

  3. 3
    Store the matches

    The result is a Python list. Each list item is one complete user Element object.

Result

The list represents all matching user subtrees and can be processed with a for loop.

Reading Each Element

findall() gives you a collection, but the useful information is inside each Element in that collection. A for loop processes the list one item at a time. During one loop iteration, the loop variable refers to a single Element object. From that object, you can use find() to locate a child element, get() to access an attribute, or .text to access text content.

Element listall matchesuserfirst Elementprocess itemfind(), get(), or .textusernext Elementuserfollowing Elementiteration 1iteration 2later iterationprocessprocessprocess
How does the for loop move from one matched XML Element to the next in the list?
user Elementone matched itemidchild elementnamechild elementxattributetext content.textcontainscontainshasexposes
What does each Element contain, and where do its text content and attributes come from?

Comparing find() and findall()

find()single searchElementfirst matchfindall()collection searchPython listall matching Elementsreturnsreturns
What is the difference between find() returning one matching Element and findall() returning multiple matching Elements?
MethodResultUse when
find()One matching Element, or None if no match existsYou need one matching element
findall()A Python list of all matching Element objectsYou need to process multiple matching elements

The key distinction is the shape of the result: one Element versus a list of Elements.

This difference determines what you do next. A result from find() can be queried as one Element. A result from findall() is a list, so you generally loop through it before querying each individual Element. Treating the list as though it were one Element confuses the collection with the objects inside the collection.

Checking the Extraction

When an extraction does not produce the expected records, first inspect the XML hierarchy and compare it with the path you supplied. The path syntax must match the structure. It is also useful to check the length of the list returned by findall() so you can confirm whether matches were found.

  • Common mistake

    Expecting findall() to return one Element

    What it looks like

    Treating the result of findall() as though it were a single user object

    Why it fails

    findall() returns a Python list containing the matching Element objects.

    Do this instead

    Iterate through the list with a for loop, then query each individual Element.

  • Common mistake

    Using find() when all matching records are needed

    What it looks like

    Searching for user elements with find() and expecting every user to be returned

    Why it fails

    find() retrieves only the first matching element, or None if no match exists.

    Do this instead

    Use findall() when the task requires multiple matching elements.

  • Common mistake

    Using a path that does not match the XML hierarchy

    What it looks like

    Using a path that skips or misorders the users container and user element

    Why it fails

    findall() follows the path provided, so an incorrect path will not identify the intended structure.

    Do this instead

    Picture the XML hierarchy first and verify the path against it.

  • Common mistake

    Assuming each loop item is already text

    What it looks like

    Trying to treat the loop variable as a text value without querying the Element

    Why it fails

    Each item is a complete Element object that may contain child elements, attributes, and text.

    Do this instead
    Use find(), get(), or .text on the current Element.
EASY

A users container contains several user elements. Explain what happens in sequence when findall() is used with the path users/user and the returned list is processed by a for loop. Include what the loop variable represents and name two ways to retrieve information from that Element.

Hints
  • Start with the result type returned by findall().
  • Describe how many times the loop runs in relation to the list.
  • Remember that the loop variable holds one complete Element during each iteration.
  • The source identifies find(), get(), and .text as ways to query an Element.

The Extraction Pattern

  1. 1
    findall() follows an XML path and returns a Python list of every matching Element object.
  2. 2
    A for loop processes the returned list one Element at a time.
  3. 3
    Each loop item is a complete Element that can be queried with find(), get(), or .text.
  4. 4
    find() returns one matching Element or None, while findall() returns a list of all matches.
  5. 5
    If the result is unexpected, verify the path against the XML hierarchy and check the length of the returned list.

Key Takeaways

  • Use findall() when you need all XML elements matching a path.
  • Expect findall() to return a list, not a single Element.
  • Use a for loop to process each matched Element individually.
  • Read child elements, attributes, and text from the current Element with find(), get(), and .text.
  • Use find() for one match and findall() for multiple matches.

Related concepts

More concepts starting with E