如何在无Excel环境的Python中提取XLS文件的文本框与图片
无Excel环境下提取XLS文件中的文本框与图片
先纠正一个关键错误
XLS是二进制OLE复合文档,不是XLSX那种ZIP压缩包,你之前用zipfile直接解压解析的方法完全不对,这就是为什么只能拿到部分文本的原因。
两种可行替代方案
方案一:纯Python解析BIFF格式(无额外软件依赖)
XLS里的绘图对象(文本框、图片)都存在工作表的OLE流中,用olefile可以读取这些流,再结合BIFF8格式规范解析记录:
import olefile import struct from io import BytesIO from PIL import Image import base64 def extract_xls_elements(xls_path, sheet_name="Sheet1"): with olefile.OleFileIO(xls_path) as ole: # 定位目标工作表的Drawing流路径 drawing_stream_path = f"Workbook/Worksheets/{sheet_name}/Drawing" if not ole.exists(drawing_stream_path): print(f"工作表{sheet_name}无绘图对象") return drawing_data = ole.openstream(drawing_stream_path).read() offset = 0 total_len = len(drawing_data) while offset < total_len: # 读取BIFF记录头:类型(2字节)+长度(2字节) rec_type = struct.unpack('<H', drawing_data[offset:offset+2])[0] rec_len = struct.unpack('<H', drawing_data[offset+2:offset+4])[0] rec_content = drawing_data[offset+4:offset+4+rec_len] # 处理文本框(对应OBJRECORD,类型0x00EC) if rec_type == 0x00EC: # 解析OBJRECORD中的文本内容,需参考BIFF8规范提取文本框的Text部分 try: # 跳过无关字段,定位文本起始位置(示例逻辑,需根据实际文件调整) text_start = rec_content.find(b'\x00\x00') + 2 text = rec_content[text_start:].decode('utf-16le').strip('\x00') print(f"文本框内容: {text}") except: print("文本框解析失败") # 处理图片(对应BLIPRECORD,类型0x00F0) elif rec_type == 0x00F0: # BLIP头占8字节,后面是原始图片数据 blip_data = rec_content[8:] # 判断图片类型:0x06=PNG,0x07=JPG blip_type = struct.unpack('<B', rec_content[0:1])[0] if blip_type == 0x06: img_format = "PNG" elif blip_type == 0x07: img_format = "JPEG" else: print(f"未知图片类型: {blip_type}") offset += 4 + rec_len continue # 转换为base64 buffered = BytesIO(blip_data) img = Image.open(buffered) buffered_out = BytesIO() img.save(buffered_out, format=img_format) img_base64 = base64.b64encode(buffered_out.getvalue()).decode("utf-8") print(f"图片base64编码: {img_base64[:50]}...") offset += 4 + rec_len # 调用示例 extract_xls_elements("ewf.xls")
注意:这个方案需要熟悉BIFF8格式的记录结构(可查微软官方BIFF文档),文本框的解析逻辑可能需要根据实际文件调整,图片解析相对稳定。
方案二:用LibreOffice无头模式转XLSX后处理(实现简单)
如果服务器允许安装LibreOffice,这是最省心的方法:先把XLS转成XLSX,再用openpyxl处理,逻辑和你之前的win32com代码几乎一致:
- 安装LibreOffice,确保
soffice命令在环境变量中 - 执行转换命令:
soffice --headless --convert-to xlsx ewf.xls
- 用
openpyxl提取内容:
from openpyxl import load_workbook import base64 from io import BytesIO wb = load_workbook("ewf.xlsx") ws = wb["Sheet1"] # 提取文本框 for shape in ws.shapes: if shape.shape_type == "textBox": print(f"文本框[{shape.name}]: {shape.text}") # 处理分组形状 elif shape.shape_type == "group": print("\n--- 分组形状内容 ---") for sub_shape in shape.groupItems: if sub_shape.shape_type == "textBox": print(f"子文本框[{sub_shape.name}]: {sub_shape.text}") # 提取图片 for img in ws._images: buffered = BytesIO() img.image.save(buffered, format="PNG") img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8") print(f"图片[{img.name}]base64: {img_base64[:50]}...")
方案对比
- 方案一:无需额外软件,但需要深入理解BIFF格式,适合严格无依赖的环境;
- 方案二:实现简单,对各种形状(包括分组)的支持更完善,适合能安装LibreOffice的场景。
内容的提问来源于stack exchange,提问作者Lee Huat Chong
相关产品推荐
相关产品推荐

