
pandas.read_xml() reads XML data and converts selected XML elements and attributes into a Pandas DataFrame. It can read XML from a file, file-like object or supported URL and can select repeating nodes with XPath.
read_xml() works best when the XML has a regular, relatively flat structure in which one repeating element represents one DataFrame row.pd.read_xml(
path_or_buffer,
xpath='./*',
namespaces=None,
elems_only=False,
attrs_only=False,
names=None,
dtype=None,
converters=None,
parse_dates=None,
encoding='utf-8',
parser='lxml',
stylesheet=None,
iterparse=None,
compression='infer',
storage_options=None
)
Some commonly used parameters are:
| Parameter | Purpose |
|---|---|
path_or_buffer | XML file path, supported URL or file-like object. |
xpath | Selects the XML elements that should become DataFrame rows. |
parser | Selects lxml or etree. |
attrs_only | Read attributes from the selected XML elements. |
elems_only | Read child elements while excluding attributes. |
namespaces | Maps namespace prefixes used by XPath to namespace URIs. |
names | Assigns column names to the resulting DataFrame. |
dtype | Controls DataFrame column data types. |
parse_dates | Converts selected columns to datetime values. |
iterparse | Processes repeating elements in very large local XML files more efficiently. |
The sample student.xml file contains repeating <Row> elements. Each Row represents one student.
import pandas as pd
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree')
print(df.head())
Output
id name class mark gender
0 1 John Deo Four 75 female
1 2 Max Ruin Three 85 male
2 3 Arnold Three 55 male
3 4 Krish Star Four 60 female
4 5 John Mike Four 60 female
Each selected <Row> becomes one DataFrame row, while its child elements become DataFrame columns.
A raw string is convenient when the XML file is stored at an absolute Windows path.
df=pd.read_xml(r'E:\testing\data\student.xml', xpath='.//Row', parser='etree')
The r prefix prevents backslashes from being interpreted as escape sequences.
The xpath parameter tells Pandas which repeating XML elements should become rows.
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree')
Here:
'.//Row'
selects all <Row> elements below the current XML root.
read_xml() expects the XPath expression to identify a collection of elements representing records rather than one individual value.If xpath is not supplied, the current default is:
'./*'
This selects the direct child elements of the XML root. Explicit XPath is useful when the required records are deeper in the document.
read_xml() supports two XML parser choices:
| Parser | Use |
|---|---|
lxml | Default parser. Supports more capable XPath expressions and XSLT stylesheets. The lxml package is required. |
etree | Uses Python's standard ElementTree parser. Suitable for simpler XPath expressions and does not require lxml. |
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree')
df=pd.read_xml('student.xml', xpath='.//Row', parser='lxml')
If required, lxml can be installed with:
python -m pip install lxml
When XML markup already exists as a Python string, wrap it in StringIO before passing it to read_xml().
from io import StringIO
import pandas as pd
xml_data='''<Students>
<Student id="1" name="John Deo" class="Four">Passed</Student>
<Student id="2" name="Max Ruin" class="Three">Failed</Student>
<Student id="3" name="Arnold" class="Three">Passed</Student>
</Students>'''
df=pd.read_xml(StringIO(xml_data), parser='etree')
print(df)
Output
id name class Student
0 1 John Deo Four Passed
1 2 Max Ruin Three Failed
2 3 Arnold Three Passed
read_xml(). Wrap string data with io.StringIO. File path strings can still be passed directly.An XML record can contain both attributes and child elements.
from io import StringIO
import pandas as pd
xml_data='''<Students>
<Student id="1">
<name>John Deo</name>
<class>Four</class>
<mark>75</mark>
</Student>
<Student id="2">
<name>Max Ruin</name>
<class>Three</class>
<mark>85</mark>
</Student>
</Students>'''
df=pd.read_xml(StringIO(xml_data), xpath='.//Student', parser='etree')
print(df)
Output
id name class mark
0 1 John Deo Four 75
1 2 Max Ruin Three 85
The id attribute and the child elements are all included.
Use attrs_only=True when the selected XML elements store the required data in attributes.
from io import StringIO
import pandas as pd
xml_data='''<Students>
<Student id="1" name="John Deo" class="Four">Passed</Student>
<Student id="2" name="Max Ruin" class="Three">Failed</Student>
<Student id="3" name="Arnold" class="Three">Passed</Student>
</Students>'''
df=pd.read_xml(StringIO(xml_data), xpath='.//Student', parser='etree', attrs_only=True)
print(df)
Output
id name class
0 1 John Deo Four
1 2 Max Ruin Three
2 3 Arnold Three
The text values Passed and Failed are not included because only attributes are requested.
elems_only=True performs the opposite type of filtering: child elements are read while attributes of the selected record elements are excluded.
from io import StringIO
import pandas as pd
xml_data='''<Students>
<Student id="1">
<name>John Deo</name>
<class>Four</class>
<mark>75</mark>
</Student>
<Student id="2">
<name>Max Ruin</name>
<class>Three</class>
<mark>85</mark>
</Student>
</Students>'''
df=pd.read_xml(StringIO(xml_data), xpath='.//Student', parser='etree', elems_only=True)
print(df)
Output
name class mark
0 John Deo Four 75
1 Max Ruin Three 85
The id attributes are excluded.
attrs_only=True and elems_only=True represent different extraction requirements. Normally choose the one that matches the XML structure instead of enabling both.XML namespaces distinguish element names that belong to different vocabularies. Supply a dictionary that maps the XPath prefix to the namespace URI.
from io import StringIO
import pandas as pd
xml_data='''<ns:Students xmlns:ns="http://example.com/ns">
<ns:Student id="1" name="John Deo" class="Four">Passed</ns:Student>
<ns:Student id="2" name="Max Ruin" class="Three">Failed</ns:Student>
</ns:Students>'''
namespaces={'ns': 'http://example.com/ns'}
df=pd.read_xml(StringIO(xml_data), xpath='.//ns:Student', namespaces=namespaces)
print(df)
Output
id name class Student
0 1 John Deo Four Passed
1 2 Max Ruin Three Failed
If an XML document declares a default namespace:
<Students xmlns="http://example.com/ns">
create a temporary prefix for use in the XPath expression:
namespaces={'doc': 'http://example.com/ns'}
df=pd.read_xml('student.xml', xpath='.//doc:Student', namespaces=namespaces)
The prefix used in Python does not have to appear literally in the original XML document. It maps to the same namespace URI.
The names parameter can assign different column names to the parsed result.
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree', names=['student_id', 'student_name', 'class_name', 'mark', 'gender'])
print(df.head())
The number and order of names must correspond to the parsed fields.
Pandas normally infers column data types. Use dtype when a particular type must be preserved.
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree', dtype={'id': 'Int64', 'mark': 'Int64'})
print(df.dtypes)
This is useful when XML values that look numeric should use a predictable DataFrame dtype.
df=pd.read_xml('student.xml', xpath='.//Row', parser='etree', converters={'name': lambda value: value.strip()})
A converter function is applied while the XML data is being read.
If an XML file contains a date field, use parse_dates to convert it to Pandas datetime values while reading.
from io import StringIO
import pandas as pd
xml_data='''<Students>
<Student><name>John Deo</name><joined>2026-01-10</joined></Student>
<Student><name>Max Ruin</name><joined>2026-02-15</joined></Student>
</Students>'''
df=pd.read_xml(StringIO(xml_data), xpath='.//Student', parser='etree', parse_dates=['joined'])
print(df.dtypes)
The joined column is parsed as a datetime column instead of remaining ordinary text.
Deeply nested XML requires additional care. read_xml() does not automatically flatten every nested hierarchy into dotted DataFrame column names.
Consider this XML:
<Students>
<Student>
<id>1</id>
<name>John Deo</name>
<marks>
<score1>75</score1>
<score2>80</score2>
</marks>
</Student>
</Students>
Reading the Student node directly:
df=pd.read_xml('nested_student.xml', xpath='.//Student', parser='etree')
does not automatically produce columns named marks.score1 and marks.score2.
If the required data is inside the <marks> element, select that level:
df=pd.read_xml('nested_student.xml', xpath='.//marks', parser='etree')
print(df)
Output
score1 score2
0 75 80
This reads the children of <marks>, but parent fields such as student ID and name are no longer automatically attached to that record.
For complex XML structures, lxml can also work with XSLT stylesheets through the stylesheet parameter to transform the XML before DataFrame conversion.
For a very large XML document, building the complete XML tree in memory may be inefficient. The iterparse parameter processes repeating nodes incrementally.
For the Plus2net student XML structure:
import pandas as pd
df=pd.read_xml('student.xml', parser='etree', iterparse={'Row': ['id', 'name', 'class', 'mark', 'gender']})
print(df.head())
The dictionary key identifies the repeating element that represents one record. The associated list specifies the descendant elements or attributes to extract.
Avoid:
df=pd.read_xml(xml_data)
when xml_data contains the actual XML markup.
Use:
from io import StringIO
df=pd.read_xml(StringIO(xml_data))
This remains valid because it represents a file path:
pd.read_xml('student.xml')
The XPath should normally select the repeating elements that represent DataFrame rows.
For:
<Students>
<Row>...</Row>
<Row>...</Row>
</Students>
use:
xpath='.//Row'
The standard etree parser supports a more limited XPath feature set. Use parser='lxml' when the XML requires more complex XPath processing.
This XML:
<ns:Student>
requires the XPath prefix to be associated with its namespace URI through namespaces.
read_xml() is most convenient for shallow, regular XML. Deep parent-child relationships may require preprocessing or a custom XML parsing step.
attrs_only=True ignores child element values. Use it only when attributes are the data you need.
elems_only=True excludes attributes from the parsed result.
If the file is extremely large and follows a repeating structure, consider iterparse instead of loading the complete XML tree.
pd.read_xml() converts XML records into a Pandas DataFrame.path_or_buffer can refer to an XML file or a file-like object../*.xpath to select the elements that should become DataFrame rows.lxml is the default parser and supports more capable XPath processing.etree is useful for simpler XML structures without requiring the lxml package.StringIO.'student.xml' can still be passed directly.attrs_only=True extracts attributes from selected XML elements.elems_only=True extracts child elements while excluding attributes.namespaces maps prefixes used by XPath to XML namespace URIs.names can provide DataFrame column names.dtype can control resulting column data types.converters can apply custom conversion functions while reading.parse_dates converts selected XML fields to datetime columns.iterparse is useful for very large local XML files with repeating records.DataFrame.to_xml() when you need to create XML from Pandas data.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.