用requests获取xml数据时应避免调用.json(),而用.text或.content获取原始响应;需检查状态码并处理命名空间、cdata及大文件流式解析。

用 requests 获取 XML 接口数据时,别直接用 .json()
XML 接口返回的不是 JSON,调用 response.json() 会抛出 JSONDecodeError。必须用 response.text 或 response.content 拿原始响应体。
常见错误现象:Expecting value: line 1 column 1 (char 0) —— 这是 requests 尝试把 XML 当 JSON 解析了。
-
response.text适合含 UTF-8 或明确声明编码的 XML(自动按响应头Content-Type解码) -
response.content返回bytes,更稳妥,尤其当服务器没正确设置charset时,可手动指定编码:response.content.decode('utf-8') - 务必检查状态码:
if response.status_code != 200:,有些 XML 接口出错时仍返回 200,但 body 是错误 XML(如<error><message>Invalid key</message></error>)
用 xml.etree.ElementTree 解析时,注意命名空间问题
很多公开 XML 接口(如 NOAA 天气、RSS、SOAP 响应)带命名空间,比如 <rss xmlns="http://purl.org/rss/1.0/"></rss>。忽略它会导致 find()、findall() 全部返回 None。
解决方法是显式传入命名空间字典:
ns = {'dc': 'http://purl.org/dc/elements/1.1/'}
root = ET.fromstring(xml_text)
items = root.findall('.//item', ns)
title = items[0].find('dc:title', ns).text if items else None
- 用
ET.register_namespace()可在输出时保留前缀,但解析时不影响查找 - 若不确定命名空间,先打印
root.tag看是否含{http://...}前缀 - 完全不想处理命名空间?可用 XPath 的通配写法:
root.findall('.//{*}item')(Python 3.8+),但可读性差,不推荐用于生产
遇到 CDATA 或特殊字符内容,text 属性可能为空
XML 中像 <description>Hello</description>
elem.text 会是 None,实际内容藏在 elem.text 之外——ElementTree 默认不解析 CDATA,而是原样保留为文本节点。- 简单场景下,直接用
ET.tostring(elem, encoding='unicode', method='html')把整个元素转成字符串再提取 - 更健壮的做法是遍历
elem.itertext():它会合并所有文本节点(包括 CDATA 内容),例如''.join(elem.itertext()).strip() - 如果 XML 含 HTML 标签且需进一步解析,别硬啃 XML,改用
BeautifulSoup配合features='xml'(需安装lxml)
需要处理大 XML 文件或流式响应时,避免一次性加载进内存
接口返回几百 MB 的 XML(如地理矢量数据、批量商品目录)时,ET.fromstring() 或 ET.parse() 会 OOM。必须用 iterparse() 边读边处理。
关键点:
- 始终调用
elem.clear()清理已处理的子元素,否则内存只增不减 - 用
events=('start', 'end')控制触发时机;通常在end事件中处理完整元素 - 不要依赖
root——iterparse()不构建完整树,只提供当前节点引用
示例片段:
context = ET.iterparse(response.content, events=('start', 'end'))
context = iter(context)
event, root = next(context) # 获取根节点,但不清除
for event, elem in context:
if event == 'end' and elem.tag == 'record':
process_record(elem)
elem.clear() # 必须!
root.clear() # 可选,进一步释放
命名空间在 iterparse() 中需手动提取,elem.nsmap 不可用,得从根元素的 elem.attrib 或正则里捞 xmlns: 声明。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











