XML扁平化是将嵌套XML转为二维结构(如DataFrame),核心是用路径生成唯一键名并处理重复元素、属性与文本混合及缺失值;需区分文本、属性、子元素三类内容,可用ElementTree递归或pandas.read_xml实现。

XML扁平化是指将嵌套的XML结构(如父子、兄弟、多层层级)转换为二维结构,常见形式是转成列表字典(list of dicts)、CSV行、或Pandas DataFrame,使每个叶子节点成为独立字段,路径信息作为列名(如 root.item.name 或 item_0_name)。
理解扁平化核心逻辑
关键不是“删掉标签”,而是把层级路径映射为唯一键名,并处理重复结构(如多个 )、文本与属性混合、以及空/缺失值。需区分三种内容:
- 元素文本(
→Alice "name": "Alice") - 元素属性(
→"item_id": "101") - 子元素结构(
→ 若有多个 item,需索引区分,如29.9 "item_0_price")
用 xml.etree.ElementTree + 递归实现基础扁平化
适合中小XML、结构相对固定。以下函数将单个XML根节点转为带路径前缀的扁平字典:
import xml.etree.ElementTree as ETdef flatten_element(elem, prefix="", result=None): if result is None: result = {}
合并当前元素的属性(加 @ 前缀避免和子元素名冲突)
for attr_key, attr_val in elem.attrib.items(): result[f"{prefix}@{attr_key}"] = attr_val # 处理文本:仅当无子元素时才取text(否则text常为缩进空白) if len(elem) == 0 and elem.text and elem.text.strip(): result[prefix] = elem.text.strip() # 递归处理子元素 for child in elem: child_prefix = f"{prefix}.{child.tag}" if prefix else child.tag # 若同名子元素出现多次,用序号区分(如 item.0.name, item.1.name) siblings = [s for s in elem if s.tag == child.tag] if len(siblings) > 1: idx = siblings.index(child) child_prefix = f"{prefix}.{child.tag}.{idx}" if prefix else f"{child.tag}.{idx}" flatten_element(child, child_prefix, result) return result使用示例
xml_str = '''
''' Laptop 999.99 Mouse 29.99 root = ET.fromstring(xml_str) flat_dict = flatten_element(root) print(flat_dict)
输出类似:{'catalog.item.0.@id': '1', 'catalog.item.0.name': 'Laptop', ...}
立即学习“Python免费学习笔记(深入)”;
用 pandas.read_xml(Python 3.11+)快速转DataFrame
如果目标是表格化(尤其含重复子节点),pandas内置支持更简洁:
import pandas as pd from io import StringIOxml_data = StringIO(xml_str)
指定重复节点为记录单位(record_path),自动扁平化属性和子元素
df = pd.read_xml(xml_data, xpath=".//item", namespaces=None, parser="etree")
自动展开:id → 列;name、price → 列;price的currency属性 → price_currency列
print(df)
id name price price_currency
0 1 Laptop 999.99 USD
1 2 Mouse 29.99 USD
立即学习“Python免费学习笔记(深入)”;
注意:
xpath=".//item"表示以每个为一行;pandas会自动把子元素文本转列,属性加后缀(如price_currency)。处理复杂场景的实用建议
真实XML常含命名空间、混合内容、CDATA、注释等,建议:
- 预处理用
ET.XMLParser(resolve_entities=False, strip_cdata=False)避免解析异常 - 命名空间存在时,在
find()或xpath中传入namespaces={"ns": "http://example.com"} - 若需保留原始顺序且字段动态,用
lxml替代标准库(支持更完整XPath、命名空间、类型推断) - 超大XML用
iterparse()流式解析,边读边扁平,避免内存溢出
不复杂但容易忽略:扁平化后列名是否可读、是否覆盖语义、缺失值如何表示(None?空字符串?),这些直接影响下游使用。按需选择递归控制粒度,或直接借力pandas/lxml成熟方案。










