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())
四、你之前代码的问题复盘
- 文件指针问题:第一次
xml_file.read()后,文件指针移到了末尾,后续的iterparse、parse都是读取空内容,所以报错“Document is empty”。 - 编码处理错误:直接替换字符串里的
不够可靠,二进制模式处理BOM才是正确姿势。 - 忽略命名空间:没有指定
ss命名空间,导致所有节点查找都返回空列表,看起来根节点没有子节点。
内容的提问来源于stack exchange,提问作者Josh77




