Concepts / Navigating XML Trees with find and findall

Navigating XML Trees with find and findall

findall retrieves a Python list of all XML nodes matching a specified path, enabling batch processing of similar structures.

  • 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 Repeated XML to Repeated Processing

When XML contains several similar structures, such as multiple users, products, or records, you usually need to process every matching node. The useful pattern is to let findall gather the matching nodes and then use a for loop to process them one at a time. This avoids writing separate extraction statements for each node.

xml

This XML has a users container with two user elements. Each user has a name child element and an x attribute. The repeated user structure is what makes findall and a loop useful.

What findall Returns

findall retrieves a Python list containing all XML nodes that match a specified path. In this example, searching for user nodes produces a list with two Element objects. Each Element represents one user node from the XML. The result is therefore a collection of nodes, not one individual node.

users XMLtwo user nodesuser pathmatching pathPython listElement, Elementfirst userElementsecond userElementsearchreturnscontainscontains
How does an XML path select multiple matching nodes, and what does the resulting Python list contain?
python
Output
lst contains two Element objects, one for each user node.

How the Loop Visits Each Node

After findall returns its list, a for loop visits each Element in sequence. The loop variable holds the current XML node. The loop body runs once for each item and stops after all items have been processed.

lsttwo ElementsitemChuck userloop completeall Elements processeditemBrent userfirst iterationnext iterationafter final item
What happens to the loop variable as the for loop visits each Element in the list?
python

On the first iteration, item represents the first user, Chuck, whose id is 001 and whose x attribute is 2. On the second iteration, item represents the second user, Brent, whose id is 009 and whose x attribute is 7. The same loop body runs twice, but item refers to a different Element on each pass.

Reading Child Text and Attributes

An XML node can contain text in a child element and attributes on its opening tag. These two kinds of data use different access patterns. Use find(child_name).text to locate a child element and retrieve the text between its tags. Use get(attribute_name) to retrieve an attribute from the current Element.

usercurrent ElementnameChuckChucktext contentx2find("name")textget("x")
How does find move from a matched user node to its name child, and where does get retrieve the x value?
python
Output
Chuck 2
Brent 7

The expression item.find("name").text first finds the name child inside the current user Element and then reads that child's text. The expression item.get("x") reads the x attribute directly from the current user Element because x is on the user opening tag rather than inside a child element.

Tracing One Complete Pass

Worked example

Processing Both User Nodes

Use the list returned by findall to process each user and extract its name text and x attribute.

  1. 1
    Gather the matches

    findall produces a Python list with two Element objects because two user nodes match the path.

  2. 2
    Begin the first iteration

    The loop variable item refers to the first user Element. Finding name and reading text produces Chuck, while get("x") produces 2.

  3. 3
    Begin the second iteration

    The loop variable is then bound to the second user Element. The same expressions produce Brent and 7.

  4. 4
    Finish the loop

    After the second Element has been processed, the loop stops because every item in the returned list has been visited.

Result

The loop processes both users without requiring separate code for each user.

findall resultPython listmatching nodesElement objectsloop variableone ElementElement methodsfind, getcontainsloop selects onecall methods on
How does the result of findall differ from a single XML node, and why does that difference determine where the loop belongs?

Diagnosing Empty or Failing Iterations

  • Common mistake

    Treating the result of findall as one XML Element

    What it looks like

    Calling element-specific extraction logic on the whole list instead of on the loop variable

    Why it fails

    findall returns a Python list containing matching Element objects. The individual Element is available during each loop iteration.

    Do this instead

    Use the list as the value after in, then call find or get on the loop variable inside the loop.

  • Common mistake

    Skipping the result check before writing extraction logic

    What it looks like

    Immediately trying to read a child value without checking how many nodes were found

    Why it fails

    A path that does not match the XML structure produces a list length of 0, so the loop body has no items to process.

    Do this instead

    Check the list length and inspect the first item immediately after findall.

  • Common mistake

    Using a path or name with the wrong spelling or capitalization

    What it looks like

    Searching for a child or attribute name that does not exactly match the XML

    Why it fails

    The source notes that XML is case-sensitive, so names must match exactly.

    Do this instead

    Compare the path, child-element name, and attribute name with the XML structure.

  • Common mistake

    Looking for an attribute as though it were a child element

    What it looks like

    Using find to look for x when x is written on the user opening tag

    Why it fails

    Child text and attributes are accessed differently. Attributes belong to the current Element.

    Do this instead

    Use item.get("x") for the x attribute and item.find("name").text for the name child text.

  1. 1
    Check the length of the list returned by findall.
  2. 2
    Inspect the first item to confirm that findall returned the expected kind of node.
  3. 3
    If the length is 0, compare the path with the XML structure.
  4. 4
    If the length is correct but the loop fails, print information about each item inside the loop.
  5. 5
    Check child-element and attribute names exactly, because XML is case-sensitive.

Practice the Extraction Pattern

EASY

Suppose findall has returned a list named lst containing the two user Elements from the example. Write the for loop statements that extract each user's name text and x attribute, then print both values.

Hints
  • The loop variable should represent one Element at a time.
  • Use find("name").text for the child text.
  • Use get("x") for the attribute.
python

The important structure is the division of responsibility: lst is the collection, item is one Element from that collection, find locates a child within item, text extracts the child's content, and get reads an attribute from item.

Key Takeaways

  1. 1
    findall returns a Python list of every XML Element matching a path.
  2. 2
    A for loop assigns one matching Element to its loop variable on each iteration.
  3. 3
    Use item.find(child_name).text to retrieve text from a child element.
  4. 4
    Use item.get(attribute_name) to retrieve an attribute from the current Element.
  5. 5
    When iteration fails, check the list length, inspect an item, and verify names and paths exactly.

At a glance

  • findall gathers all matching XML nodes into a Python list.
  • The for loop processes one Element from that list at a time.
  • find followed by text reads child-element content, while get reads an attribute.
  • Debugging begins by checking whether findall returned the expected number and type of nodes.

More concepts starting with N