XML格式的数据在接口返回、配置文件、数据交换等场景中非常常见,比如网站地图sitemap、RSS订阅源、各类Web服务的返回结果。以往用Python处理这类数据,大多数人会选择ElementTree或者lxml逐个节点遍历,再自己拼装成列表和字典,代码写起来相当繁琐。从Pandas 1.3版本开始,官方提供了一个专门的处理函数read_xml,它可以直接把XML文件、XML字符串甚至URL地址解析成DataFrame,省去了大量手写解析逻辑的工作。这篇文章就来把这个函数的用法讲透。

read_xml基础用法与参数说明
先看最简单的场景。假设有一个XML文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<root>
<row>
<name>张三</name>
<age>28</age>
<city>北京</city>
</row>
<row>
<name>李四</name>
<age>34</age>
<city>上海</city>
</row>
<row>
<name>王五</name>
<age>25</age>
<city>广州</city>
</row>
</root>读取这个文件只需要一行代码:
import pandas as pd
df = pd.read_xml("data.xml")
print(df)输出结果会自动把<row>下的子标签识别成列名,每个<row>节点变成一行数据。这个默认行为依赖于函数内部的两步推断:先找到文档中重复出现的子节点作为行节点,再把行节点下的子元素名作为列名。
read_xml常用的参数有这么几个,了解之后能应付绝大多数场景:
- path_or_buffer:可以是本地文件路径、XML字符串、URL或者文件对象
- xpath:指定从哪个节点开始解析,默认是
"./*",表示根节点下的所有子节点 - attrs_only:设为True时只提取节点的属性,不提取子元素
- elems_only:设为True时只提取子元素文本,不提取属性
- namespaces:传入命名空间的字典,处理带前缀的XML
- parser:选择解析器,默认是lxml,也可以改成etree
- encoding:指定文件编码,中文文件建议明确指定为utf-8
除了读文件,直接传XML字符串也可以:pd.read_xml(xml_string),这在处理接口返回的数据时特别方便,不用先落盘再读取。
用xpath定位节点和提取属性
实际遇到的XML结构往往不是那么规整,行节点不一定直接挂在根节点下面。这时候就需要用xpath参数来精确定位。比如下面这个带一层包裹的结构:
<response>
<data>
<record>
<id>1001</id>
<value>56.7</value>
</record>
<record>
<id>1002</id>
<value>43.2</value>
</record>
</data>
<status>ok</status>
</response>如果直接读取,<status>节点会干扰解析。正确做法是用xpath跳过<data>这一层:
df = pd.read_xml("response.xml", xpath=".//data/record")xpath的写法遵循标准语法,.//表示从当前位置向下任意层级搜索,data/record表示逐级定位。如果文档里有多层同名节点嵌套,用xpath可以把不同层级的数据分开读成多个DataFrame再合并,这是处理复杂嵌套结构的常用套路。
属性提取是另一个常见需求。XML里很多数据不是写在标签文本里,而是放在属性上,例如:
<books>
<book id="101" price="45.5">Python入门</book>
<book id="102" price="38.0">数据分析师养成</book>
</books>这种情况下默认读取只能拿到book的文本内容。想拿到属性值,可以这样:
# 只要属性列
df = pd.read_xml("books.xml", xpath=".//book", attrs_only=True)
# 属性和文本都要
df = pd.read_xml("books.xml", xpath=".//book")
print(df.columns.tolist())当attrs_only不设置时,read_xml会同时提取属性和子元素,属性列用@属性名的形式表示(有些版本直接用属性名),文本内容则归入以标签名命名的列。建议实际运行时先打印df.columns确认列名,再做后续的字段映射。
处理命名空间和嵌套结构的实用技巧
带命名空间的XML是最容易踩坑的地方。比如RSS或Atom格式的文档,节点上往往带着dc:、atom:这类前缀,直接读取会抛出找不到节点的错误。解决办法是通过namespaces参数把前缀和URI的对应关系传进去:
df = pd.read_xml(
"feed.xml",
xpath=".//item",
namespaces={"dc": "http://purl.org/dc/elements/1.1/"}
)命名空间的URI必须和XML文档头部声明里的完全一致,少一个斜杠或者大小写不对都会匹配失败。如果不确定URI是什么,先用文本编辑器打开XML文件,查看根节点上的xmlns声明即可。
再说说嵌套结构。当行节点下面还有更深层的子节点时,read_xml会把深层节点的文本直接拍平成一列,列名就是子节点的标签名。如果遇到同名标签出现在不同层级,或者需要把深层结构拆成子表,建议先用etree或者lxml把原始结构打印出来,理清层级关系后再决定是一次读取还是分层读取后用merge拼接。
最后提两个常见报错。ValueError: Either attrs_only or elems_only说明这两个参数不能同时设为True,只能二选一或者都不设;如果提示缺少lxml,说明环境里没装这个库,用pip install lxml装上即可,或者把parser参数改成"etree"用标准库解析。另外读取中文文件遇到乱码时,显式加上encoding="utf-8"基本都能解决。掌握这几个要点之后,大部分XML到DataFrame的转换工作都可以用read_xml轻松搞定。