Python自动化处理Excel图片:插入、提取与压缩实战
1. Python操作Excel图片的完整指南作为数据分析师我每天都要处理大量包含图片的Excel报表。手动操作不仅效率低下还容易出错。Python的openpyxl和Pillow库彻底改变了我的工作方式 - 现在我能用代码批量插入产品图、自动压缩图片节省空间还能从几百个文件中一键提取所有图片。下面分享我积累的完整解决方案。2. 环境准备与工具选型2.1 必备库安装与版本选择推荐使用Python 3.8环境这是目前企业级应用最稳定的版本。通过pip安装以下核心库pip install openpyxl3.0.10 # Excel操作核心库 pip install pillow9.5.0 # 图片处理神器 pip install python-pptx0.6.21 # 如需处理PPT注意openpyxl 3.1.0版本对图片处理有重大变更建议锁定3.0.x版本确保兼容性。我曾在新版本踩过坑部分图片插入功能会出现异常。2.2 开发工具配置VSCode是我的主力工具配置建议安装Python扩展设置Jupyter Notebook交互支持添加Excel预览插件如Excel Viewer# 验证环境是否正常 import openpyxl from PIL import Image print(openpyxl.__version__, Image.__version__) # 应显示3.0.10和9.5.03. 图片插入实战技巧3.1 基础插入方法from openpyxl.drawing.image import Image as XLImage def insert_image(file_path, img_path, cell_location): wb openpyxl.load_workbook(file_path) ws wb.active img XLImage(img_path) # 创建图片对象 ws.add_image(img, cell_location) # 如B2 wb.save(file_path) print(f图片已插入到{cell_location})典型问题处理图片不显示检查路径是否含中文/特殊字符位置偏移Excel默认以单元格左上角为锚点大小异常需提前用Pillow调整尺寸3.2 高级排版控制精确控制图片位置和尺寸的参数from openpyxl.utils.units import pixels_to_EMU, cm_to_EMU img.width pixels_to_EMU(300) # 宽度300像素 img.height pixels_to_EMU(200) # 高度200像素 # 厘米转Excel单位 img.left cm_to_EMU(2.5) # 距左2.5cm img.top cm_to_EMU(1.8) # 距上1.8cm实测案例为电商报告插入商品图时保持所有图片宽度统一为5cm间距1cm代码实现def batch_insert_images(ws, img_folder): for i, img_file in enumerate(os.listdir(img_folder)): img_path os.path.join(img_folder, img_file) img XLImage(img_path) # 统一处理尺寸 img.width cm_to_EMU(5) img.height cm_to_EMU(5 * img.height/img.width) # 保持比例 # 计算位置 (每行3张图) row i // 3 col i % 3 img.left cm_to_EMU(1 col*6) img.top cm_to_EMU(1 row*6) ws.add_image(img)4. 图片提取技术解析4.1 从Excel提取图片的两种方案方案一直接解析.xlsx文件.xlsx本质是zip压缩包图片存储在xl/media目录import zipfile def extract_images_xlsx(file_path, output_dir): with zipfile.ZipFile(file_path) as z: for file in z.namelist(): if file.startswith(xl/media/): z.extract(file, output_dir) print(f图片已保存到{output_dir})方案二使用openpyxl遍历图形对象适合需要获取图片位置信息的场景def get_image_positions(ws): images [] for img in ws._images: images.append({ anchor: img.anchor._from, size: (img.width, img.height), path: img.path # 仅在新版本支持 }) return images踩坑记录旧版Excel(.xls)需要使用xlrd库但微软已停止支持。建议统一转为.xlsx格式处理。4.2 提取图片的命名优化默认提取的图片名称为image1.png、image2.jpg等。改进方案from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing def extract_with_better_names(file_path): wb openpyxl.load_workbook(file_path) drawing SpreadsheetDrawing.from_tree(wb._images[0]) for rel in drawing._rels: img_data wb._images[rel.target] img_name f{ws.title}_{rel.id}.{img_data.format.lower()} with open(img_name, wb) as f: f.write(img_data._data())5. 图片压缩的工业级方案5.1 质量与尺寸平衡使用Pillow进行智能压缩from PIL import Image import io def compress_image(img_path, quality85, max_size(1024,1024)): img Image.open(img_path) # 等比例缩放 img.thumbnail(max_size, Image.Resampling.LANCZOS) # 优化存储 buffer io.BytesIO() img.save(buffer, formatJPEG, qualityquality, optimizeTrue, progressiveTrue) return buffer.getvalue()参数说明quality: 85是视觉无损的临界点optimize: 启用额外压缩算法progressive: 渐进式加载优化5.2 批量压缩Excel内图片完整工作流def compress_excel_images(input_path, output_path): # 临时目录处理 temp_dir temp_images os.makedirs(temp_dir, exist_okTrue) # 提取原图 extract_images_xlsx(input_path, temp_dir) # 创建新工作簿 wb openpyxl.Workbook() ws wb.active # 压缩并重新插入 for img_file in os.listdir(temp_dir): img_path os.path.join(temp_dir, img_file) compressed compress_image(img_path) with open(img_path, wb) as f: f.write(compressed) img XLImage(img_path) ws.add_image(img) # 清理并保存 shutil.rmtree(temp_dir) wb.save(output_path)实测数据一个包含50张手机照片的Excel文件原始大小28MB经压缩后仅3.5MB体积减少87%。6. 企业级应用案例6.1 电商商品报告自动化需求场景每日从ERP导出含SKU的Excel自动匹配图片库中的商品图生成带图标的采购清单def generate_product_report(data_excel, img_folder, output_file): # 加载数据 df pd.read_excel(data_excel) wb openpyxl.Workbook() ws wb.active # 写入表头 ws.append([SKU, 名称, 单价, 库存, 图片]) # 遍历商品 for idx, row in df.iterrows(): img_path find_image_by_sku(img_folder, row[sku]) if img_path: img XLImage(img_path) img.width pixels_to_EMU(100) img.height pixels_to_EMU(100) ws.add_image(img, fE{idx2}) # 从第2行开始 ws.append([row[sku], row[name], row[price], row[stock]]) # 压缩并保存 compress_excel_images(wb, output_file)6.2 财务报表图片水印为敏感报表添加动态水印from PIL import ImageDraw, ImageFont def add_watermark(img_path, text): img Image.open(img_path) draw ImageDraw.Draw(img) font ImageFont.truetype(arial.ttf, 40) textwidth, textheight draw.textsize(text, font) # 计算水印位置 x img.width - textwidth - 10 y img.height - textheight - 10 # 半透明效果 draw.text((x, y), text, fontfont, fill(255, 255, 255, 128)) return img7. 性能优化与异常处理7.1 大文件处理技巧当Excel超过50MB时使用read_only模式加载分块处理图片及时清理内存def process_large_excel(file_path): # 只读模式打开 wb openpyxl.load_workbook(file_path, read_onlyTrue) try: for sheet in wb: # 分块处理逻辑 process_sheet(sheet) finally: # 确保资源释放 wb.close() del wb7.2 常见错误排查图片破损现象插入后显示红叉解决方案用Pillow验证图片完整性try: Image.open(img_path).verify() except Exception as e: print(f图片损坏: {e})位置错乱现象图片覆盖单元格修复检查锚点单位是否混淆pixels vs EMU内存泄漏现象处理大量图片时内存激增优化使用生成器逐张处理def image_generator(folder): for file in os.listdir(folder): yield Image.open(os.path.join(folder, file))8. 扩展应用与进阶技巧8.1 与PPT联动使用python-pptx库实现跨Office自动化from pptx import Presentation def excel_to_ppt(excel_path, ppt_template, output_ppt): prs Presentation(ppt_template) # 提取Excel图片 images extract_images(excel_path) # 插入PPT for img_data in images: slide prs.slides.add_slide(prs.slide_layouts[1]) pic slide.shapes.add_picture( io.BytesIO(img_data), leftInches(1), topInches(1.5)) prs.save(output_ppt)8.2 生成动态图表结合matplotlib创建带图表的Excelimport matplotlib.pyplot as plt def add_matplotlib_chart(ws, data): fig, ax plt.subplots() ax.plot(data[x], data[y]) # 保存为图片缓冲区 buffer io.BytesIO() fig.savefig(buffer, formatpng) buffer.seek(0) # 插入Excel img XLImage(buffer) ws.add_image(img, A10) plt.close(fig)9. 安全注意事项文件操作安全检查用户上传的Excel是否包含恶意宏使用tempfile模块处理临时文件图片安全验证图片实际格式防止伪装的.exedef check_image_safety(file_path): try: img Image.open(file_path) img.verify() return True except: return False权限管理敏感操作添加水印记录图片操作日志10. 完整项目结构示例excel_image_tool/ ├── core/ │ ├── __init__.py │ ├── extractor.py # 图片提取逻辑 │ ├── inserter.py # 图片插入逻辑 │ └── compressor.py # 压缩处理 ├── utils/ │ ├── file_utils.py # 文件操作 │ └── image_utils.py # Pillow扩展 ├── config.py # 全局配置 └── main.py # 命令行入口典型命令行接口# main.py import click click.command() click.option(--input, help输入Excel文件) click.option(--output, help输出路径) def cli(input, output): # 业务逻辑整合 pass if __name__ __main__: cli()使用方式python main.py --input report.xlsx --output out/