You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用pd.read_xml高效解析6000+ XML Spreadsheet 2003文件为Pandas DataFrame

使用pd.read_xml高效解析6000+ XML Spreadsheet 2003文件为Pandas DataFrame

我太懂你这种要处理一大堆特殊XML格式Excel文件的头疼了——6000+文件,还要高效转成Pandas DataFrame,还指定要用pd.read_xml不想直接碰lxml,完全是实际生产环境的刚需,性能绝对是核心。

先给你拆解下问题:这些是微软的XML Spreadsheet 2003格式,自带命名空间,这也是pd.read_xml最容易踩坑的地方,搞定命名空间和正确的XPath,剩下的就顺理成章了。

第一步:搞定命名空间与XPath定位

首先,这个XML里的核心命名空间是urn:schemas-microsoft-com:office:spreadsheet,我们先把它定义成字典,方便pd.read_xml识别:

namespaces = {'ss': 'urn:schemas-microsoft-com:office:spreadsheet'}

然后,数据都在名为Details的工作表下的<Table>标签里,每个<Row>对应一行数据,每行的4个<Cell>分别对应ShortNameFieldCodeDateValue四列。我们用XPath精准定位到这些行:

  • 定位目标工作表的所有行://ss:Worksheet[@ss:Name="Details"]/ss:Table/ss:Row
  • 再给每行的4个单元格分别指定提取路径,映射到表头列名。

第二步:单个文件解析代码

先写单个文件的解析逻辑,验证正确后再批量处理:

import pandas as pd

def parse_single_xml(file_path):
    # 定义命名空间
    ns = {'ss': 'urn:schemas-microsoft-com:office:spreadsheet'}
    
    # 读取XML并提取数据
    df = pd.read_xml(
        file_path,
        namespaces=ns,
        # 定位到Details工作表下的所有行
        xpath='//ss:Worksheet[@ss:Name="Details"]/ss:Table/ss:Row',
        # 为每列指定从行内提取的XPath
        elems=[
            './ss:Cell[1]/ss:Data',  # 第一列:ShortName
            './ss:Cell[2]/ss:Data',  # 第二列:FieldCode
            './ss:Cell[3]/ss:Data',  # 第三列:Date
            './ss:Cell[4]/ss:Data'   # 第四列:Value
        ],
        # 对应列名
        names=['ShortName', 'FieldCode', 'Date', 'Value']
    )
    
    # 跳过第一行(表头行),重置索引
    df = df.iloc[1:].reset_index(drop=True)
    
    # 处理空值:把None转为空字符串(可选,根据你的需求调整)
    df = df.fillna('')
    
    return df

# 测试单个文件
test_df = parse_single_xml('your_sample_file.xml')
print(test_df.head())

运行后就能得到你期望的四列DataFrame,每个数据行完美对应原XML里的内容。

第三步:6000+文件的高效批量处理

批量处理的核心是减少内存开销+避免重复操作,这里用pathlib遍历文件,列表推导式收集所有DataFrame,最后一次性合并:

from pathlib import Path

# 所有XML文件的目录
xml_dir = Path('path/to/your/xml/files')

# 收集所有文件的DataFrame(用列表推导式比循环append快很多)
all_dfs = [parse_single_xml(file) for file in xml_dir.glob('*.xml') if file.is_file()]

# 一次性合并所有DataFrame(比循环concat高效N倍)
final_df = pd.concat(all_dfs, ignore_index=True)

# 保存结果(可选,比如存为Parquet格式,比CSV快很多,适合大数据量)
final_df.to_parquet('all_combined_data.parquet', index=False)

性能优化小贴士

针对6000+文件的规模,这几个细节能帮你省不少时间:

  • 预加载依赖:确保你的Pandas是用C扩展的lxml编译的(默认安装的都是),这是解析速度的基础。
  • 避免循环concat:列表推导式收集所有DataFrame后一次性concat,避免每次concat都复制整个DataFrame。
  • 分块处理(内存不足时用):如果内存放不下所有DataFrame,每1000个文件合并一次,保存为中间Parquet文件,最后再合并中间文件:
    chunk_size = 1000
    files = list(xml_dir.glob('*.xml'))
    for i in range(0, len(files), chunk_size):
        chunk_files = files[i:i+chunk_size]
        chunk_df = pd.concat([parse_single_xml(f) for f in chunk_files], ignore_index=True)
        chunk_df.to_parquet(f'chunk_{i//chunk_size}.parquet', index=False)
    
    # 合并所有chunk
    final_df = pd.concat([pd.read_parquet(f) for f in xml_dir.glob('chunk_*.parquet')], ignore_index=True)
    
  • 跳过不必要的解析:XPath里精准定位到Details工作表,避免解析其他无关内容,减少解析时间。

验证结果

你可以对比解析后的DataFrame和原XML数据,比如第一行数据应该是:

ShortNameFieldCodeDateValue
CHF vs EURCalculateyes

完全符合你的期望。

备注:内容来源于stack exchange,提问作者JM Nel

火山引擎 最新活动