You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python中XML转DataFrame解析失败求助:疑似编码问题导致无法处理Excel XML文件

解决Excel XML(.xls后缀)导入Pandas DataFrame的问题

我明白你折腾了两天有多头疼,这种披着.xls外衣的Excel XML文件确实容易踩坑,咱们一步步把问题拆解开解决:

一、先搞定文件读取与编码问题

你遇到的重复的UTF-8 BOM(字节顺序标记),这是导致编码解析失败的核心原因之一。之前用文本模式读取文件会自动处理BOM,但重复的BOM会残留下来,而且你第一次读取文件后指针移到了末尾,后续的readlines()根本读不到内容,这也是chardet返回None的原因。

正确的处理方式是用二进制模式读取,手动去除重复的BOM,再转成UTF-8字符串:

import lxml.etree as et
import pandas as pd
import codecs

# 二进制读取文件,保留原始字节
with open('C:\\MSCI.xml', 'rb') as f:
    raw_content = f.read()

# 去除开头的重复UTF-8 BOM(对应一个BOM,这里有两个重复的)
cleaned_content = raw_content.lstrip(codecs.BOM_UTF8 * 2)

# 转成UTF-8编码的字符串
xml_str = cleaned_content.decode('utf-8')

二、解析XML并处理命名空间

这个XML是微软的SpreadsheetML格式,所有核心节点都在ss命名空间下,不指定命名空间的话,你根本找不到任何子节点,这就是你之前根节点看起来为空的关键问题!

现在解析XML并定义命名空间字典:

# 用lxml解析XML,开启recover模式容错小的格式问题
parser = et.XMLParser(recover=True)
root = et.fromstring(xml_str, parser=parser)

# 定义命名空间,后续所有节点查找都要用到
ns = {"ss": "urn:schemas-microsoft-com:office:spreadsheet"}

三、提取工作表数据并转成DataFrame

接下来我们遍历每个工作表,提取ss:Table里的行和单元格,注意处理单元格的ss:Index属性(用来跳过空列),最后把数据整理成Pandas DataFrame:

# 存储所有工作表的DataFrame,键是工作表名称
all_sheets = {}

# 遍历所有工作表
for worksheet in root.findall('ss:Worksheet', namespaces=ns):
    sheet_name = worksheet.attrib['ss:Name']
    table = worksheet.find('ss:Table', namespaces=ns)
    rows = table.findall('ss:Row', namespaces=ns)
    
    sheet_rows = []
    for row in rows:
        cells = row.findall('ss:Cell', namespaces=ns)
        current_row = []
        current_col = 0
        
        for cell in cells:
            # 获取单元格的列索引(默认是当前列+1)
            cell_index = int(cell.attrib.get('ss:Index', current_col + 1))
            # 填充空列(比如单元格直接从第3列开始,前面两列是空的)
            while current_col < cell_index - 1:
                current_row.append(None)
                current_col += 1
            
            # 提取单元格数据
            data_node = cell.find('ss:Data', namespaces=ns)
            current_row.append(data_node.text if data_node is not None else None)
            
            current_col += 1
        
        sheet_rows.append(current_row)
    
    # 转成DataFrame,如果你需要把第一行设为表头,可以额外处理
    df = pd.DataFrame(sheet_rows)
    # 可选:把第一行作为表头,然后跳过第一行数据
    # df.columns = df.iloc[0]
    # df = df[1:].reset_index(drop=True)
    
    all_sheets[sheet_name] = df

# 示例:打印第一个工作表的前5行
print(all_sheets['Overzicht'].head())

四、你之前代码的问题复盘

  1. 文件指针问题:第一次xml_file.read()后,文件指针移到了末尾,后续的iterparseparse都是读取空内容,所以报错“Document is empty”。
  2. 编码处理错误:直接替换字符串里的不够可靠,二进制模式处理BOM才是正确姿势。
  3. 忽略命名空间:没有指定ss命名空间,导致所有节点查找都返回空列表,看起来根节点没有子节点。

内容的提问来源于stack exchange,提问作者Josh77

火山引擎 最新活动