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.
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
- 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.
- 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.
- 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.
- find(path) returns the first match or None; iter(tag) visits every element in document order, including the root.
- 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].
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.
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.
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.
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.
Chuck 2
Brent 7The 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
Processing Both User Nodes
Use the list returned by findall to process each user and extract its name text and x attribute.
- 1Gather the matches
findall produces a Python list with two Element objects because two user nodes match the path.
- 2Begin 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. - 3Begin the second iteration
The loop variable is then bound to the second user Element. The same expressions produce Brent and 7.
- 4Finish the loop
After the second Element has been processed, the loop stops because every item in the returned list has been visited.
The loop processes both users without requiring separate code for each user.
Diagnosing Empty or Failing Iterations
Common mistake Treating the result of findall as one XML Element
What it looks likeCalling element-specific extraction logic on the whole list instead of on the loop variable
Why it failsfindall returns a Python list containing matching Element objects. The individual Element is available during each loop iteration.
Do this insteadUse 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 likeImmediately trying to read a child value without checking how many nodes were found
Why it failsA 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 insteadCheck 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 likeSearching for a child or attribute name that does not exactly match the XML
Why it failsThe source notes that XML is case-sensitive, so names must match exactly.
Do this insteadCompare 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 likeUsing find to look for x when x is written on the user opening tag
Why it failsChild text and attributes are accessed differently. Attributes belong to the current Element.
Do this insteadUse item.get("x") for the x attribute and item.find("name").text for the name child text.
- 1Check the length of the list returned by findall.
- 2Inspect the first item to confirm that findall returned the expected kind of node.
- 3If the length is 0, compare the path with the XML structure.
- 4If the length is correct but the loop fails, print information about each item inside the loop.
- 5Check child-element and attribute names exactly, because XML is case-sensitive.
Practice the Extraction Pattern
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.
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
- 1findall returns a Python list of every XML Element matching a path.
- 2A for loop assigns one matching Element to its loop variable on each iteration.
- 3Use
item.find(child_name).text to retrieve text from a child element. - 4Use
item.get(attribute_name)to retrieve an attribute from the current Element. - 5When 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.