Pandas读取.xls文件报XLRDError格式不支持错误如何批量解决
报错信息里的b'\xef\xbb\xbf<?xml'是核心定位标识:你拿到的这批.xls后缀文件,并非传统二进制格式的Excel 97-2003文件,而是被错误标注了.xls后缀的XML格式表格(绝大多数是Excel 2003 SpreadsheetML格式,少数是带表格结构的HTML文件)。
这类文件是老旧公共系统导出时的常见产物:系统生成的是XML/HTML结构的表格内容,直接硬加了.xls后缀。Excel打开时会自动探测文件真实格式,所以会弹出后缀不匹配的提示,点确认就能正常打开,手动另存时Excel会把内容转成标准二进制xls或OOXML xlsx格式,pandas自然能正常读取。
你之前尝试的openpyxl、xlrd、xls2xlsx等库都是按后缀判断解析逻辑,上来就按标准二进制xls/OOXML xlsx的结构去解析,自然会报BOF记录不匹配的错误。另外你贴的示例代码本身存在写法错误,后面会单独说明。
按优先级从高到低排列,优先选无依赖、速度快的方案:
方案1:自动识别格式直接读取(推荐,跨平台、速度最快)
使用支持自动探测文件真实格式的calamine解析引擎,完全无视错误后缀的影响,不需要做任何转存操作,批量处理效率是所有方案里最高的。
- 安装依赖:
pip install pandas>=2.1 python-calamine - 读取代码示例,直接指定engine参数即可:
import pandas as pd # 直接读取指定工作表,自动识别文件真实格式,不需要改后缀、不需要转存 first_sheet = pd.read_excel( "file_name.xls", sheet_name="First Tab Name", engine="calamine" )
这个方案不依赖Microsoft Office组件,Windows、Mac、Linux环境都能跑,对各种错后缀的Excel格式兼容性拉满。
方案2:Windows环境批量转标准xlsx(兼容性最强)
如果你的运行环境是Windows,且已经安装了Microsoft Excel,可以直接调用Excel COM接口做批量转换,和手动点另存的效果完全一致,哪怕文件里有复杂公式、特殊格式也不会错乱。
转换代码示例:
import os import win32com.client as win32 def batch_convert_to_xlsx(input_folder, output_folder): # 初始化Excel进程,后台运行不弹窗口 excel = win32.gencache.EnsureDispatch("Excel.Application") excel.Visible = False excel.DisplayAlerts = False os.makedirs(output_folder, exist_ok=True) for file_name in os.listdir(input_folder): if not file_name.lower().endswith(".xls"): continue file_path = os.path.abspath(os.path.join(input_folder, file_name)) # 打开文件时Excel自动识别真实格式,忽略后缀不匹配的提示 wb = excel.Workbooks.Open(file_path, ReadOnly=True) output_path = os.path.abspath( os.path.join(output_folder, file_name.replace(".xls", ".xlsx")) ) # FileFormat=51 对应标准xlsx格式 wb.SaveAs(output_path, FileFormat=51) wb.Close() excel.Quit()
转换完成后直接用pandas读输出目录里的标准xlsx文件即可,不会有任何报错。
方案3:按真实格式单独解析(无需额外安装大依赖)
如果不方便装calamine,也可以先读文件头判断真实格式,再选对应的解析方式:
import pandas as pd def read_fake_xls(file_path, sheet_name=0): with open(file_path, "rb") as f: file_header = f.read(1024).lower() # 判断是HTML格式的假xls if b"<html" in file_header: # read_html返回所有工作表对应的df列表,按sheet序号取即可 all_sheets = pd.read_html(file_path) return all_sheets[sheet_name] if isinstance(sheet_name, int) else all_sheets[0] # 判断是XML格式的SpreadsheetML,走openpyxl的xml解析逻辑 elif file_header.startswith(b"\xef\xbb\xbf<?xml"): return pd.read_excel(file_path, sheet_name=sheet_name, engine="openpyxl") # 是标准xls,走默认逻辑 else: return pd.read_excel(file_path, sheet_name=sheet_name) # 调用示例 first_sheet = read_fake_xls("file_name.xls", sheet_name="First Tab Name")
- 你贴的原始代码存在逻辑错误:
wb = pd.read_excel("file_name.xls")会直接读取文件默认第一个工作表,返回的是DataFrame对象,不是工作簿实例。再把这个DataFrame作为文件路径传入第二个read_excel调用本身就会触发参数错误,读取指定工作表直接给read_excel传sheet_name参数即可,不需要分两次调用。 - 用zipfile解压文件时,可以在解压后先校验文件头,对这类错后缀的假xls做标记,避免后续读取时走错误的解析逻辑。
- 不要用2.0以上版本的xlrd读取这类文件,xlrd新版本只支持标准二进制xls格式,遇到错后缀的文件必然触发你看到的BOF record报错。
内容的提问来源于stack exchange,提问作者Ollamacare

