Navigating XML Trees with findall() and XPath
ET.fromstring() converts an XML string into a tree structure of Element objects, making the hierarchical data queryable.
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 Text to Tree
XML begins as text, but its nested tags describe a hierarchy. ET.fromstring() converts that XML string into a tree structure made of Element objects. After conversion, you work with the hierarchy rather than treating the data as one undivided string.
Suppose the XML describes a person with name, phone, and email elements. After tree = ET.fromstring(data), tree is an Element object representing the person root. The tree contains the complete hierarchy, including the three child Elements. It is not simply the original string printed in another form; it is a structured object that you query with methods.
Tracing the Root Element
tree = ET.fromstring(data)- 1Before this line, data is an XML string.
- 2During this line,
ET.fromstring()converts the string into a tree structure. - 3After this line, tree represents the person root and its descendants.
ET.fromstring(data)parses the XML string.- tree becomes the root Element of the parsed hierarchy.
- For the source example, the root Element is person.
Selecting an Element
The find() method searches the tree for the first element matching a given tag name. Starting with the root Element, you provide the tag name you want to retrieve. For example, tree.find('name') searches for the name element in the person hierarchy.
ChuckThe two lines separate retrieval from extraction. First, find() produces the name Element. Then .text reads the text inside that Element. In the source example, tree.find('name').text returns Chuck.
Reading Text and Attributes
An Element can hold text content and attributes. Use the .text property to retrieve the text inside an element. Use .get(attribute_name) to retrieve the value of a named attribute. These are two different kinds of information: text belongs to the element's content, while an attribute value is retrieved by its attribute name.
| Expression | What it accesses | Source result |
|---|---|---|
| tree.find('name').text | Text content of the name Element | Chuck |
| tree.find('email').get('hide') | Value of the hide attribute on the email Element | yes |
The source example separates element text from attribute retrieval.
XPath and Multiple Matches
The topic of this article also includes findall() and XPath as ways of navigating XML trees. The supplied reference facts explain the conversion step, find(), .text, and .get() in detail, but do not specify the exact findall() or XPath syntax or matching rules. Keep the verified distinction clear: find() is documented here as returning the first element matching a tag name.
When reading XML code, trace it in this order: identify the Element that represents the current tree, identify the tag or navigation expression being used, identify the selected Element, and only then read .text or .get() to determine the extracted value.
Mistakes Beginners Make
Common mistake Treating the result of ET.fromstring() as an ordinary string
What it looks likeAssuming tree is still the original XML text after tree = ET.fromstring(data)
Why it failsThe conversion produces an Element object representing the root and its hierarchy.
Do this insteadUse Element methods and properties such as find(), .text, and .get().
Common mistake Confusing the Element with its text content
What it looks likeUsing the result of tree.find('name') as if it were already the text Chuck
Why it failsfind()returns an element, while .text retrieves the content inside that element.Do this insteadUse tree.find('name').text when you need the text value.
Common mistake Using .text to retrieve an attribute
What it looks likeTrying to read the hide value on the email element with .text
Why it failsThe source example retrieves the hide attribute with .get('hide').
Do this insteadUse tree.find('email').get('hide') for the attribute value.
Common mistake Ignoring preserved whitespace
What it looks likeUsing text exactly as returned when the XML contains surrounding whitespace
Why it failsWhitespace in XML text is preserved.
Do this insteadUse .strip() when surrounding whitespace needs to be removed.
Common mistake Assuming find() represents every possible match
What it looks likeTreating tree.find('name') as a collection of matching elementsWhy it failsThe documented behavior here is that
find()searches for the first matching element.Do this insteadInterpret the result of find() as one selected Element, and consult the specific API documentation when using findall() or XPath.
Practice the Trace
Using the source example's person tree, trace each expression and state what kind of result it produces: tree = ET.fromstring(data), tree.find('name'), tree.find('name').text, and tree.find('email').get('hide').
Hints
- The first expression performs the conversion.
- The second expression selects an Element.
- The third expression reads text from the selected Element.
- The fourth expression reads an attribute value from the email Element.
Tracing the Source Example
Determine what each operation represents when the XML root is person with name, phone, and email children.
- 1Convert
ET.fromstring(data)turns the XML string into an Element tree whose root is person. - 2Select
tree.find('name')searches the tree for the first element matching the name tag. - 3Read text
Adding .text retrieves the text inside the name Element, which is Chuck in the source example.
- 4Read attribute
tree.find('email').get('hide') retrieves the hide attribute value from the email Element, which is yes in the source example.
The conversion creates the structure, find() selects an Element, .text reads element content, and .get() reads an attribute value.
Key Takeaways
- 1
ET.fromstring()converts an XML string into a hierarchy of Element objects. - 2The resulting root Element represents the complete XML tree and its descendants.
- 3
find()searches for the first element matching a tag name. - 4.text retrieves an element's text content, while .get(
attribute_name) retrieves an attribute value. - 5XML whitespace is preserved, so use .strip() when surrounding whitespace must be cleaned.
At a glance
ET.fromstring()changes linear XML text into a queryable Element tree.- The root Element contains the hierarchy represented by the XML.
find()returns the first Element matching the requested tag name.- Use .text for element content and .get() for attribute values.
- Remember that XML whitespace is preserved and may require .strip().