当Word文档里的图片成了“拦路虎”:用Python批量处理图片的实战指南
你有没有遇到过这样的场景:部门例会前,领导丢给你一个文件夹,里面是几十个Word文档,每个文档里都散落着好几张活动照片。你的任务很简单——把所有图片提取出来,统一调整尺寸,再按照特定的顺序塞进一个新的报告文档里。
听起来不难,对吧?但当你打开第一个文档,一张张右键另存为,再调整尺寸,再复制粘贴到新文档……重复几十次之后,你会开始怀疑人生。手头的工作变成了纯粹的体力劳动,而且只要有一个环节出错,就得从头再来。
这种时候,Python就派上用场了。它不会抱怨重复劳动,也不会因为眼花点错图片。我花了两个小时写了一小段脚本,把原本一整天的手工活压缩成了三分钟。今天就把这段经历和代码拆开来讲,希望能帮你摆脱同样的困境。
准备工作:装好工具箱
在动手之前,得先把需要用到的工具装上。Python的好处就是有大量的第三方库,专门解决这类问题。这次我们需要三个帮手:
pip install python-docx pillow
python-docx是我们操作Word文档的主力。它可以读取文档里的文字、表格、图片,也能从头创建新的文档或者修改现有的文档。Pillow是Python图像处理库,负责调整图片尺寸、转换格式这些活儿。
安装完成之后,在代码开头把这些库引进来:
from docx import Document
from docx.shared import Inches, Cm
from PIL import Image
import io
import os
Inches和Cm是用来设置图片大小的单位,io帮我们处理内存里的图片数据,os负责管理文件和文件夹路径。
从Word文档里“捞”出图片
Word文档和图片的关系,比看起来要复杂一点。你可能会想,图片不就是放在文档里的吗?但当我们用代码去读取时,需要找到正确的“门路”。
每个.docx文件其实是个压缩包,图片被藏在内部的media文件夹里,通过XML关系与文档内容关联。python-docx提供了一个方法,可以顺着这个关系把图片提取出来。
下面这段代码能遍历一个文件夹里所有的Word文档,把每张图片提取出来,按照“文档名_图片序号.jpg”的格式保存:
def extract_images_from_docx(folder_path):
# 遍历文件夹里所有docx文件
for filename in os.listdir(folder_path):
if not filename.endswith('.docx'):
continue
doc_path = os.path.join(folder_path, filename)
doc = Document(doc_path)
# 准备一个文件夹存放提取出的图片
img_folder = os.path.join(folder_path, filename.replace('.docx', '_images'))
if not os.path.exists(img_folder):
os.makedirs(img_folder)
img_count = 0
# 遍历文档中的所有段落
for paragraph in doc.paragraphs:
for run in paragraph.runs:
# 在run的XML中查找图片标记
blip = run._r.find('.//a:blip',
namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
if blip is not None:
embed_id = blip.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
if embed_id and embed_id in run.part.related_parts:
image_part = run.part.related_parts[embed_id]
img_stream = io.BytesIO(image_part.blob)
img = Image.open(img_stream)
img_count += 1
img_path = os.path.join(img_folder, f'image_{img_count}.jpg')
img.save(img_path, 'JPEG')
print(f'已提取: {img_path}')
print(f'{filename} 处理完成,共提取 {img_count} 张图片')
这段代码的核心是那个看起来很复杂的XML查找。简单解释一下:Word文档的内部结构是用XML描述的,图片会被标记为<a:blip>元素,通过embed属性关联到实际的图片数据。我们把图片数据从内存里读出来,用Pillow打开,再保存到本地,就完成了提取。
批量处理图片:统一尺寸和格式
提取出来的图片可能大小不一,有的横版有的竖版,如果直接塞进文档里会很难看。我们可以统一调整尺寸,让它们都变成一样的宽度,高度按比例自动缩放。
def resize_images(image_folder, output_width=500):
# 获取文件夹里所有图片
image_files = [f for f in os.listdir(image_folder)
if f.endswith(('.jpg', '.jpeg', '.png'))]
for img_file in image_files:
img_path = os.path.join(image_folder, img_file)
img = Image.open(img_path)
# 计算等比例缩放后的高度
original_width, original_height = img.size
new_height = int(output_width * original_height / original_width)
# 使用高质量缩放算法
resized_img = img.resize((output_width, new_height), Image.Resampling.LANCZOS)
# 覆盖原图或者保存为新文件
resized_img.save(img_path, quality=95)
print(f'已调整尺寸: {img_file} -> {output_width}x{new_height}')
这里用了Image.Resampling.LANCZOS,这是Pillow里质量最高的缩放算法,虽然慢一点,但效果最好。如果你的图片数量特别多,可以考虑用Image.Resampling.BILINEAR换速度。
把图片塞回新文档
图片都准备好了,现在要把它们按照顺序插进一个新的Word文档里。python-docx插入图片的方法很直观,用add_picture就行:
def create_photo_report(image_folder, output_path='report.docx'):
# 创建新文档
doc = Document()
# 添加标题
doc.add_heading('活动照片集锦', 0)
# 获取所有图片文件并排序
image_files = sorted([f for f in os.listdir(image_folder)
if f.endswith(('.jpg', '.jpeg', '.png'))])
for img_file in image_files:
img_path = os.path.join(image_folder, img_file)
# 添加一个段落,放图片说明
doc.add_heading(img_file.replace('_', ' ').replace('.jpg', ''), level=2)
# 插入图片,宽度设为6英寸,高度自动缩放
doc.add_picture(img_path, width=Inches(6))
# 添加一点留白
doc.add_paragraph()
doc.save(output_path)
print(f'报告已生成: {output_path}')
add_picture的width参数可以接受Inches、Cm或Pt单位。如果你想让图片按固定高度插入,也可以用height参数。只指定一个维度时,另一个维度会自动等比缩放,不用自己算。
进阶技巧:在指定位置插入图片
有些时候,光是把图片堆在文档末尾不够用。你可能需要把图片插到某个段落后边,或者放到表格的某个单元格里。
在段落里插图片,关键是先拿到那个段落,然后在它的run里添加:
# 假设我们要在文档的第一个段落之后插入图片
doc = Document('existing.docx')
target_paragraph = doc.paragraphs[0] # 获取第一个段落
run = target_paragraph.add_run() # 在段落中创建一个新run
run.add_picture('image.jpg', width=Inches(3)) # 插入图片
在表格里插图片也类似,先定位到单元格,再操作:
table = doc.add_table(rows=2, cols=2)
cell = table.cell(0, 0) # 第一行第一列
paragraph = cell.paragraphs[0]
run = paragraph.add_run()
run.add_picture('image.jpg', width=Cm(5))
需要注意的是,python-docx对图片位置的精确控制能力有限,没办法像手动操作那样任意拖拽到指定坐标。如果对排版有很高要求,可以先通过代码把图片插入到位,再用Word手动微调。
处理那些烦人的“坑”
实际应用中总会遇到一些意外情况,比如文档里有浮动图片、文档是旧的.doc格式等等。这里说几个常见坑的应对方法。
浮动图片提取不到:上面那种通过段落查找的方法只能提取“嵌入型”图片。如果文档里有浮在文字上方的图片,需要更底层的操作。可以用python-docx的part.related_parts遍历所有资源,找到图片类型就保存。一个更简单但笨一点的办法是:先把文档另存为.docx,用压缩软件打开,直接去word/media文件夹里复制图片。
遇到.doc格式:python-docx只支持.docx,处理不了旧版.doc文件。可以先用win32com库在Windows上转换,或者用LibreOffice的命令行工具批量转换。如果只是偶尔遇到,手动转一下可能更快。
图片插入后变形:这通常是因为给图片设置了固定的宽高,但比例和原图不一致。解决办法是只设置一个维度,让另一个维度自动缩放。如果必须指定两个维度,可以先用Pillow读取原图尺寸,再手动计算缩放后的宽高。
写在最后
回到开头的场景,当你再次面对几十个需要处理图片的Word文档时,你可以选择继续手工操作,也可以写一段脚本,然后去泡杯咖啡,等着它帮你把一切做完。
Python自动化最大的魅力就在这里——它不是为了炫技,而是为了把我们从重复劳动里解放出来,去做那些真正需要思考和创造的事情。这次我们只用了python-docx和Pillow两个库,但你能做的事情远不止提取和插入图片。
想想你日常工作里那些“反复做、费时间、有规律”的事情,很可能都可以用类似的方式自动化。批量生成合同、整理数据报表、统一修改文档格式……这些场景里,Python都能成为你的得力助手。