如何使用 Python 操作 Excel 图片:插入、提取与压缩
那天下午我对着500张产品图发愁事情是这样的。上个月公司要做一份产品目录需要把500多款产品的图片插进Excel表格里每个产品对应一行图片放在第二列大小还要统一。我打开Excel手动插了一张图——调整大小、拖到单元格里、对齐。看了一眼右下角的时间我算了一笔账一张图算30秒500张就是250分钟整整4个多小时。关键是这活儿毫无技术含量纯属机械重复。那一刻我意识到这事儿必须用Python解决。后来我用几行代码搞定了这个任务顺便还研究了怎么从Excel里提取图片、怎么压缩图片大小。今天就把这些经验整理出来希望对你有用。准备工作先装好这些库在动手之前需要安装几个Python库。不同任务需要的库不太一样但有几个是通用的# 基础图像处理 pip install pillow # 操作Excel文件选一个就行 pip install openpyxl # 处理.xlsx文件不支持图片插入 pip install xlwings # 需要本地有Excel软件 pip install spire.xls.free # 支持图片操作无需Excel环境需要说明的是openpyxl这个库虽然很常用但它对图片的操作能力非常有限——只能读取已有图片无法插入或删除。如果你想要完整控制图片的增删改推荐用spire.xls.free或者xlwings。还有个更轻量的选择叫pywin32它可以直接调用Windows上的Excel程序来干活适合在Windows电脑上跑。插入图片把图片精准放到单元格里回到开头那个问题如何批量把图片插入Excel并且精准对齐到每个单元格这里用一个叫xlwings的库来实现。它的原理是启动你电脑上的Excel程序然后像人工操作一样往里填东西。import xlwings as xw import os # 打开Excel文件 wb xw.Book(产品目录.xlsx) sheet wb.sheets[产品列表] # 图片存放的文件夹 image_folder product_images/ # 从第2行开始假设第1行是表头 for i, filename in enumerate(os.listdir(image_folder), start2): if filename.endswith((.png, .jpg)): # 构建图片完整路径 img_path os.path.join(image_folder, filename) # 把图片插入到B列当前行 sheet.pictures.add(img_path, leftsheet.range(fB{i}).left, topsheet.range(fB{i}).top, width100, # 设置宽度 height80) # 设置高度 # 可选调整行高让图片不重叠 sheet.range(f{i}:{i}).row_height 80 wb.save() wb.close() print(搞定图片已全部插入)这段代码的核心逻辑很简单遍历文件夹里的所有图片把它们依次放到Excel的B列从第2行开始往下排。left和top参数让图片的左上角对齐到单元格的左上角这样位置就精准了。如果你的需求更复杂比如需要把图片放到特定的单元格而不是按顺序可以改成这样# 假设有一个字典键是产品编号值是对应图片路径 product_map { A001: images/A001.jpg, A002: images/A002.jpg, # ... } for product_id, img_path in product_map.items(): # 找到产品编号在哪一行 cell sheet.range(A:A).find(product_id) if cell: row cell.row sheet.pictures.add(img_path, leftsheet.range(fC{row}).left, topsheet.range(fC{row}).top, width100, height80)这个逻辑是先找到产品编号所在的单元格位置再把图片插到对应的C列。无论表格顺序怎么变图片都不会放错。提取图片把Excel里的图片薅出来有时候反过来Excel文件里已经有一堆图片需要把它们导出到文件夹里。这个场景常见于从别人发来的报表中提取素材。openpyxl这个库虽然不能插入图片但读取图片还是可以的。不过它只能处理.xlsx格式的文件老版的.xls就不行了。from openpyxl import load_workbook from openpyxl_image_loader import SheetImageLoader import os # 加载Excel文件 wb load_workbook(带图片的报告.xlsx) sheet wb.active # 创建保存图片的文件夹 output_dir extracted_images os.makedirs(output_dir, exist_okTrue) # 用ImageLoader提取所有图片 image_loader SheetImageLoader(sheet) # 遍历所有单元格 for row in range(1, sheet.max_row 1): for col in range(1, sheet.max_column 1): cell sheet.cell(row, col) if image_loader.image_in(cell.coordinate): img image_loader.get(cell.coordinate) # 保存图片用行列命名 img.save(f{output_dir}/img_{row}_{col}.png) print(f已保存第{row}行第{col}列的图片) print(f提取完成图片保存在{output_dir}文件夹)如果你用的是Spire.XLS库提取图片的代码会更简洁from spire.xls import * workbook Workbook() workbook.LoadFromFile(带图片的报告.xlsx) sheet workbook.Worksheets[0] for i, picture in enumerate(sheet.Pictures): picture.Picture.Save(fextracted_images/pic_{i}.png) workbook.Dispose()这里有个坑需要注意如果Excel里的图片是“浮”在单元格上方的而不是“嵌入”在单元格里的上面的方法可能抓不到。这种情况下可以考虑用pywin32直接操作Excel程序来提取。压缩图片让臃肿的Excel文件瘦身图片插多了Excel文件会变得巨大。一个几兆的Excel插了几十张高清图后可能变成几百兆打开都费劲。压缩图片的核心思路就是降低图片质量。用PILPillow库可以轻松做到from PIL import Image import os def compress_image(input_path, output_path, quality50): 压缩图片 quality: 1-100数值越小压缩越狠文件越小 img Image.open(input_path) # 保存时指定qualityJPEG格式支持压缩 img.save(output_path, JPEG, qualityquality) # 看看压缩效果 original_size os.path.getsize(input_path) / 1024 compressed_size os.path.getsize(output_path) / 1024 print(f{os.path.basename(input_path)}: f{original_size:.1f}KB → {compressed_size:.1f}KB)这个函数可以把一张几兆的图片压缩到几十KB肉眼几乎看不出差别。quality参数是关键设为50左右能兼顾文件大小和清晰度。如果有多张图片需要批量压缩可以这样def batch_compress(folder_path, quality50): for filename in os.listdir(folder_path): if filename.endswith((.png, .jpg, .jpeg)): input_path os.path.join(folder_path, filename) output_path os.path.join(folder_path, fcompressed_{filename}) compress_image(input_path, output_path, quality)还有个更高级的玩法多阶段压缩。如果一次压缩达不到目标大小就逐步降低质量甚至缩小尺寸直到文件够小为止。def compress_to_target(img_path, target_kb100, max_attempts5): 反复压缩直到文件小于target_kb quality 80 for attempt in range(max_attempts): temp_path ftemp_{attempt}.jpg compress_image(img_path, temp_path, quality) if os.path.getsize(temp_path) / 1024 target_kb: # 压缩成功 os.replace(temp_path, img_path) return True # 还不够小继续降低质量 quality - 15 os.remove(temp_path) return False如果你用的是Spire.XLS库它自带了图片压缩方法可以直接在Excel内部压缩不需要先导出再插回from spire.xls import * workbook Workbook() workbook.LoadFromFile(需要压缩图片的表格.xlsx) for sheet in workbook.Worksheets: for picture in sheet.Pictures: # 直接压缩图片参数是压缩比例 picture.Compress(50) workbook.SaveToFile(压缩后的表格.xlsx, ExcelVersion.Version2016) workbook.Dispose()这种方式的好处是操作简单而且压缩后的图片还待在原来的位置不用重新排版。进阶从图片表格里提取文字到Excel还有一种情况很常见你手里有一张图片格式的表格比如扫描件、截图想把它转成可编辑的Excel。这就用到OCR技术了。Tesseract是一个开源的OCR引擎配合Python的pytesseract库可以搞定import pytesseract from PIL import Image import pandas as pd # 如果Tesseract没在系统路径里需要指定一下 pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe def image_to_excel(img_path, output_excel): # 读取图片 img Image.open(img_path) # 识别文字chi_sim是中文简体模型 text pytesseract.image_to_string(img, langchi_simeng) # 把识别结果按行分割 lines text.strip().split(\n) # 转换成DataFrame并保存 df pd.DataFrame([line.split(\t) for line in lines]) df.to_excel(output_excel, indexFalse, headerFalse) print(f识别完成结果保存在{output_excel}) # 使用示例 image_to_excel(表格截图.png, 识别结果.xlsx)这种方法的识别精度取决于图片质量。如果图片不够清晰可以先用PIL做一下预处理def preprocess_image(img_path): img Image.open(img_path) # 转为灰度图 img img.convert(L) # 提高对比度 from PIL import ImageEnhance enhancer ImageEnhance.Contrast(img) img enhancer.enhance(2.0) return img如果你想达到更高的识别准确率比如98%以上可以考虑用商业OCR接口比如百度OCR。它专门优化过表格识别场景还能自动解析表格的行列结构。不过这是付费服务有免费额度适合企业级应用。避坑指南这些细节不注意会翻车路径问题Windows路径里的反斜杠\需要转义或者直接用正斜杠/。保险的做法是img_path rC:\Users\name\images\product.jpg # 或者 img_path C:/Users/name/images/product.jpgExcel被占用如果你的代码运行时报错说文件被占用记得先把Excel关掉。xlwings这类库启动Excel程序时如果文件已经打开可能会冲突。图片格式兼容.png支持透明背景.jpg压缩率高但不支持透明。插入到Excel里两种都能用但压缩时要注意PNG转JPEG会丢失透明通道。VBA权限问题如果用pywin32的方式插入图片需要提前在Excel里开启“信任对VBA项目对象模型的访问”权限。具体路径Excel选项 → 信任中心 → 信任中心设置 → 宏设置 → 勾选这个选项。这步不做会报错。内存溢出批量处理大量图片时如果电脑内存不够用可以考虑分批处理。比如一次处理100张处理完释放内存再继续。收个尾回头来看那天下午让我发愁的500张产品图最后用Python不到一分钟就跑完了。代码跑起来的那一刻看着屏幕上的进度条噌噌往前走那种感觉确实挺爽的。Excel和图片打交道这件事用Python来搞无非就是三个核心操作插进去、薅出来、压一压。掌握了这几个套路以后再遇到类似的活儿就不用跟Excel较劲了。希望这篇文章能帮你省下几个小时的摸鱼时间。代码都在上面了复制粘贴改改路径就能用。如果有问题欢迎交流讨论。