Accessing XML Attributes and Elements
The findall method retrieves a list of XML nodes using a path that must include all parent-level elements (except the root) separated by forward slashes.
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…
The Path Determines the Result
When you retrieve repeated XML elements, the important question is not only what element you want, but also where that element is located. The findall method returns a list of matching nodes, but its path must describe the parent levels between the current node and the target. If user elements are inside a users element, the path is users/user. The root element is usually omitted because the search begins from the current node, which is the root in this situation.
Finding the User Nodes
lst = stuff.findall('users/user')- 1The search starts at stuff, the current node.
- 2Python follows the users child element.
- 3Within users, it finds the user child elements.
- 4The result is stored in lst as a list of user nodes.
- stuff is the current XML tree node
- findall searches using the complete parent-to-child path
- users identifies the parent element
- user identifies the target elements
The path users/user does not mean that there is one single user. It describes the route to the target level: first users, then each user beneath it. The returned list contains the matching user nodes. A path such as user asks for user elements that are direct children of the current node. When users is the direct child and user is nested inside users, that shortened path does not reach the target.
Moving Through the Node List
The for loop visits each node in the list one at a time and in the order in which the nodes appear in the XML document. During one iteration, item refers to one user node. The find method searches inside that node for a child element. Adding .text retrieves the text inside that child element. The get method reads an attribute attached directly to the current user node.
| Current node | Child-element text | Attribute value |
|---|---|---|
| first user | name Chuck; id 001 | x is 2 |
| second user | name Brent; id 009 | x is 7 |
Values extracted as the loop processes the two user nodes
Separating Text from Attributes
An XML node can contain both child elements and attributes, but they are accessed differently. In the user node with x="2", x is an attribute on the user opening tag, so item.get('x') retrieves its value. The name and id values are inside child elements such as name and id, so item.find('name').text and item.find('id').text retrieve their text content. The choice between find and get depends on where the data is stored.
| XML location | Access pattern | Value in the first user node |
|---|---|---|
| Child element | item.find('name').text | Chuck |
| Child element | item.find('id').text | 001 |
| Attribute | item.get('x') | 2 |
Use find followed by .text for child-element content and get for an attribute
Diagnosing Path Mistakes
Common mistake Leaving out the parent level
What it looks likestuff.findall('user')Why it failsThe search looks for user elements directly under the current node, but the user elements are inside users.
Do this insteadUse stuff.findall('users/user').Common mistake Using the parent name as the target path
What it looks likestuff.findall('users')Why it failsThis searches for users nodes rather than the user nodes nested inside them.
Do this insteadInclude the target level with stuff.findall('users/user').
Common mistake Using the wrong element name
What it looks likestuff.findall('users/member')Why it failsThe path does not name the user elements that exist in the structure.
Do this insteadCheck each element name and use the actual path users/user.
Common mistake Confusing an attribute with a child element
What it looks likeitem.find('x') for the x valueWhy it failsx is an attribute on the user node, not a child element.
Do this insteadUse item.get('x') for the attribute.
lst = stuff.findall('user')
for item in lst:
print(item)lst is empty, so the loop never executes
lst = stuff.findall('users/user')
for item in lst:
print(item)lst contains the user nodes, so the loop processes them in document order
A Complete Extraction Pass
Read Each User Record
Retrieve the name, id, and x attribute from every user node beneath users.
- 1Locate the repeated nodes
Use
findall('users/user')so the path includes the users parent and reaches the user children. - 2Process the first node
The loop assigns the first user node to item. Its child text values are Chuck and 001, and its x attribute is 2.
- 3Process the second node
The loop then assigns the second user node to item. Its child text values are Brent and 009, and its x attribute is 7.
- 4Finish the loop
After the second node, the list has no remaining nodes, so iteration ends.
The two user nodes are visited once each, in document order, and each node supplies its name text, id text, and x attribute.
What do you think happens?
If findall returns an empty list, what happens when the for loop runs?
Reveal answer
The loop never executes
A for loop processes the items that are present in the list. An empty list has no items, so there are no iterations and no output from the loop.
Practice and Verification
Suppose a tree has a users element containing user elements. Write the findall statement that retrieves the user nodes, then write the two expressions needed to obtain the name text and the x attribute from the current loop variable item.
Hints
- Include both users and user in the path, separated by a forward slash.
- Use find with the child-element name and follow it with .text.
- Use get with the attribute name.
- 1To extract repeated XML records, start at the current node and write every parent-level element needed to reach the target, leaving out the root itself. Use
findall('users/user')to receive a list of user nodes. A for loop visits those nodes one at a time and in document order. Within each iteration, useitem.find('child').text for text inside a child element anditem.get('attribute')for an attribute attached directly to the current node. If the path is incomplete or names the wrong level, findall may return an empty list and the loop may produce no output.
Key Takeaways
- findall returns a list of nodes reached through the specified parent-to-child path.
- The path normally omits the root because the search starts from the current node.
- A for loop processes each returned node once and in document order.
- Use find followed by .text for child-element content and get for attributes.
- An incomplete or incorrect path can return an empty list without an error.