那天下午,我对着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), start=2):
if filename.endswith(('.png', '.jpg')):
# 构建图片完整路径
img_path = os.path.join(image_folder, filename)
# 把图片插入到B列当前行
sheet.pictures.add(img_path,
left=sheet.range(f'B{i}').left,
top=sheet.range(f'B{i}').top,
width=100, # 设置宽度
height=80) # 设置高度
# 可选:调整行高让图片不重叠
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,
left=sheet.range(f'C{row}').left,
top=sheet.range(f'C{row}').top,
width=100,
height=80)
这个逻辑是先找到产品编号所在的单元格位置,再把图片插到对应的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_ok=True)
用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(f'extractedimages/pic{i}.png')
workbook.Dispose()
这里有个坑需要注意:如果Excel里的图片是“浮”在单元格上方的,而不是“嵌入”在单元格里的,上面的方法可能抓不到。这种情况下可以考虑用pywin32直接操作Excel程序来提取。
压缩图片:让臃肿的Excel文件瘦身
图片插多了,Excel文件会变得巨大。一个几兆的Excel,插了几十张高清图后可能变成几百兆,打开都费劲。
压缩图片的核心思路就是降低图片质量。用PIL(Pillow)库可以轻松做到:
from PIL import Image
import os
def compress_image(input_path, output_path, quality=50):
"""
压缩图片
quality: 1-100,数值越小压缩越狠,文件越小
"""
img = Image.open(input_path)
# 保存时指定quality,JPEG格式支持压缩
img.save(output_path, 'JPEG', quality=quality)
# 看看压缩效果
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, quality=50):
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(folderpath, f'compressed{filename}')
compress_image(input_path, output_path, quality)
还有个更高级的玩法:多阶段压缩。如果一次压缩达不到目标大小,就逐步降低质量,甚至缩小尺寸,直到文件够小为止。
def compress_to_target(img_path, target_kb=100, max_attempts=5):
"""反复压缩直到文件小于target_kb"""
quality = 80
for attempt in range(max_attempts):
temppath = f'temp{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 = r'C:\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, lang='chi_sim+eng')
# 把识别结果按行分割
lines = text.strip().split('\n')
# 转换成DataFrame并保存
df = pd.DataFrame([line.split('\t') for line in lines])
df.to_excel(output_excel, index=False, header=False)
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 = r'C:\Users\name\images\product.jpg'
或者
img_path = 'C:/Users/name/images/product.jpg'
Excel被占用
如果你的代码运行时报错说文件被占用,记得先把Excel关掉。xlwings这类库启动Excel程序时,如果文件已经打开可能会冲突。
图片格式兼容
.png支持透明背景,.jpg压缩率高但不支持透明。插入到Excel里两种都能用,但压缩时要注意:PNG转JPEG会丢失透明通道。
VBA权限问题
如果用pywin32的方式插入图片,需要提前在Excel里开启“信任对VBA项目对象模型的访问”权限。具体路径:Excel选项 → 信任中心 → 信任中心设置 → 宏设置 → 勾选这个选项。这步不做会报错。
内存溢出
批量处理大量图片时,如果电脑内存不够用,可以考虑分批处理。比如一次处理100张,处理完释放内存再继续。
收个尾
回头来看,那天下午让我发愁的500张产品图,最后用Python不到一分钟就跑完了。代码跑起来的那一刻,看着屏幕上的进度条噌噌往前走,那种感觉确实挺爽的。
Excel和图片打交道这件事,用Python来搞无非就是三个核心操作:插进去、薅出来、压一压。掌握了这几个套路,以后再遇到类似的活儿,就不用跟Excel较劲了。
希望这篇文章能帮你省下几个小时的摸鱼时间。代码都在上面了,复制粘贴改改路径就能用。如果有问题,欢迎交流讨论。