Python-docx批量写入Word:图片表格自动化脚本实战
发布时间:2026/10/23 17:47:28 锦皓数字建站

简介这份资源面向需要批量处理Word文档的Python学习者与办公自动化从业者聚焦用python-docx库把图片和表格高效写入.docx文件解决重复手工排版、逐份生成文档的痛点适合具备Python基础、希望提升文档处理效率的初中级开发者。压缩包共12个文件约367KB包含10个docx示例文档、1个py脚本和1个jpg图片docx用于展示批量生成的工资调整通知效果py脚本承载图片遍历与表格填充的核心逻辑jpg则作为插入图片的素材。目前已有181人学习下载。读者可从中获得可直接运行的批量处理脚本理解Document创建、add_picture插图、add_table建表与cell填充、doc.save保存等关键用法并借助os库遍历目录的思路把同一套流程迁移到通知、报表、台账等场景快速搭建属于自己的文档自动化方案。1. 批量把图片和表格塞进 Word一份能直接跑的 python-docx 脚本包手里攒了三十几份巡检记录每份都要把现场照片和检测数据表贴进对应的 Word 模板里手动复制粘贴到第三份的时候我就知道这事不能这么干。这份资源就是冲着这个场景来的一个基于 python-docx 的批量写入脚本包把散落在文件夹里的图片和表格按规则灌进 .docx省掉重复劳动。它适合两类人——一类是天天跟报表、台账、检测记录打交道的办公岗另一类是想拿 python-docx 练手但不知道从哪下手的开发者。核心逻辑不复杂难的是图片尺寸、表格样式、中文字体这些细节脚本包里都做了预设改几个路径就能跑。2. 拆开脚本包python-docx 写入图片和表格的底层逻辑2.1 为什么选 python-docx 而不是直接操作 XMLWord 的 .docx 本质是个 zip 包里面是一堆 XML。理论上你可以用 zipfile 解压、改 document.xml、再压回去但这条路走一次就不想走第二次——命名空间、关系映射、rId 分配任何一处对不上Word 打开就报“内容有问题”。python-docx 把这层封装掉了它把 document.xml 里的段落、表格、图片关系都抽象成 Python 对象你调 add_picture、add_table 就行。常见做法是用 python-docx 做主体写入遇到它不支持的冷门样式比如某些域代码、复杂页眉再退回 lxml 操作底层 XML。这份脚本包走的就是这个路线主体用 python-docx个别地方用 OxmlElement 补了边框和底纹。选型上还有一个考虑是依赖轻。python-docx 只依赖 lxml 和 typing-extensionspip 装完就能用不需要装 Office也不需要 COM 接口。这意味着它能在 Linux 服务器上跑也能塞进定时任务里。如果你之前用 win32com 操作 Word应该体会过那种“必须开着 Word 进程、偶尔卡死、报错信息看不懂”的滋味python-docx 在这点上干净得多。2.2 图片写入尺寸、对齐和路径的三个参数先看图片写入的核心代码。脚本包里把这段封装成了insert_images(doc, img_dir, width_cm12)from docx import Document from docx.shared import Cm from docx.enum.text import WD_ALIGN_PARAGRAPH import os def insert_images(doc, img_dir, width_cm12): 把 img_dir 下的图片按文件名排序后逐张插入 doc width_cm: 图片宽度单位厘米高度按比例自动缩放 # 只取常见图片格式避免把缩略图缓存也读进来 exts (.png, .jpg, .jpeg, .bmp) files sorted( f for f in os.listdir(img_dir) if f.lower().endswith(exts) ) for fname in files: path os.path.join(img_dir, fname) # 先加一个空段落再往段落里加图片方便控制对齐 para doc.add_paragraph() para.alignment WD_ALIGN_PARAGRAPH.CENTER run para.add_run() # 只给宽度高度自动等比缩放避免图片被拉变形 run.add_picture(path, widthCm(width_cm)) # 图片下方加一行文件名做标注不需要可删 cap doc.add_paragraph(fname) cap.alignment WD_ALIGN_PARAGRAPH.CENTER逻辑说明add_picture只传 width 的时候python-docx 会按图片原始宽高比自动算高度这是最稳的做法。如果你同时传 width 和 height图片会被强行拉伸人像和表格截图会变形。参数width_cm默认 12 厘米对应 A4 纸正文宽度约 15.9 厘米留出边距后的常见值你可以按模板调整。路径处理上有个细节os.listdir返回的顺序在不同系统上不一致所以脚本里加了sorted。如果你的图片文件名是img1.jpg、img2.jpg、img10.jpg这种字符串排序会把 img10 排到 img2 前面这时候需要改成按数字排序import re files.sort(keylambda x: int(re.search(r\d, x).group()))这个坑我在第一次批量插巡检照片的时候就踩了十张照片顺序全乱后来加了自然排序才对上。2.3 表格写入从列表数据到带样式的 Word 表格表格写入比图片多一层结构。脚本包里insert_table(doc, data, headersNone, styleTable Grid)的实现def insert_table(doc, data, headersNone, styleTable Grid): data: 二维列表如 [[A, 1], [B, 2]] headers: 表头列表为 None 时不生成表头行 style: Word 内置表格样式名Table Grid 带全边框 rows len(data) (1 if headers else 0) cols len(headers) if headers else len(data[0]) table doc.add_table(rowsrows, colscols) table.style style if headers: for j, h in enumerate(headers): cell table.cell(0, j) cell.text str(h) # 表头加粗 for p in cell.paragraphs: for r in p.runs: r.bold True offset 1 if headers else 0 for i, row in enumerate(data): for j, val in enumerate(row): table.cell(i offset, j).text str(val) return table逻辑说明add_table创建的是空表格行列数必须提前算好。styleTable Grid是 Word 内置样式名带全边框最常用。如果你想要三线表得手动设边框python-docx 没有直接的三线表样式需要用 OxmlElement 改 tblBorders脚本包里附了这段代码。参数上要注意data的每一行长度必须一致否则len(data[0])算出来的列数会对不上写入时索引越界。我一般会在调用前加一句校验assert len(set(len(r) for r in data)) 1, 每行列数不一致中文字体是另一个高频问题。python-docx 默认字体是 Calibri中文会回退到宋体但如果你在模板里指定了字体写入的表格可能不继承。稳妥做法是显式设置from docx.oxml.ns import qn style doc.styles[Normal] style.font.name Times New Roman style.element.rPr.rFonts.set(qn(w:eastAsia), 宋体)这段要放在创建文档之后、写入内容之前否则已经写入的段落不会更新。2.4 批量循环一份数据对应一份文档的目录约定脚本包的主入口是batch_generate(template_path, data_root, out_dir)目录约定是这样的data_root/ record_001/ images/ # 该记录的图片 table.csv # 该记录的表格数据 record_002/ ...主循环代码import csv from docx import Document def batch_generate(template_path, data_root, out_dir): os.makedirs(out_dir, exist_okTrue) for case in sorted(os.listdir(data_root)): case_dir os.path.join(data_root, case) if not os.path.isdir(case_dir): continue # 每份文档都从模板复制一份保留页眉页脚和样式 doc Document(template_path) img_dir os.path.join(case_dir, images) if os.path.isdir(img_dir): insert_images(doc, img_dir) csv_path os.path.join(case_dir, table.csv) if os.path.exists(csv_path): with open(csv_path, encodingutf-8-sig) as f: reader list(csv.reader(f)) insert_table(doc, reader[1:], headersreader[0]) doc.save(os.path.join(out_dir, f{case}.docx))逻辑说明Document(template_path)是打开模板而不是新建空文档这样模板里的页眉、页脚、样式、页面设置都会保留。encodingutf-8-sig是为了兼容 Excel 导出的 CSV 带 BOM 头的情况不加这个参数第一列会多一个不可见字符。参数out_dir建议和data_root分开避免输出文件被下一轮循环当成输入。如果你要覆盖原文件记得先备份。3. 避坑排查图片不显示、表格错位、字体乱掉的五个血泪经验3.1 图片插入后 Word 显示红叉或空白现象脚本跑完没报错打开 docx 图片位置是红叉或空白框。原因python-docx 插入图片时是把图片二进制写进 docx 包里的正常不会丢。出现红叉通常是图片本身损坏或者扩展名和实际格式不符——比如把 webp 改成 jpg 后缀python-docx 按 jpg 解析失败但没抛异常。解决插入前用 PIL 校验一遍from PIL import Image try: Image.open(path).verify() except Exception as e: print(f跳过损坏图片 {path}: {e}) continue3.2 表格列宽在 Word 里被自动调整现象脚本里没设列宽Word 打开后表格列宽忽宽忽窄跟预期不符。原因python-docx 创建表格时如果不设列宽Word 会按“自动调整”渲染列宽取决于内容长度。table.autofit False只是关掉自动调整还需要显式设每列宽度。解决创建表格后逐列设置table.autofit False for j, w in enumerate([Cm(3), Cm(5), Cm(4)]): for cell in table.columns[j].cells: cell.width w注意cell.width要每个单元格都设只设column.width在某些 Word 版本上不生效。3.3 中文字体在表格里变成宋体标题字体没继承现象正文段落字体正常表格里的中文全变成宋体和模板不一致。原因表格单元格的段落用的是 Normal 样式但 python-docx 写入时没有显式指定 eastAsia 字体Word 回退到默认中文字体。解决写入表格后遍历单元格统一设字体for row in table.rows: for cell in row.cells: for p in cell.paragraphs: for r in p.runs: r.font.name 微软雅黑 r.element.rPr.rFonts.set(qn(w:eastAsia), 微软雅黑)3.4 批量跑完发现文档数量对不上现象data_root 下有 30 个文件夹out_dir 只生成了 28 个 docx。原因通常是文件夹名排序后中间某个文件夹里没有 images 也没有 table.csv脚本跳过了或者文件夹名含特殊字符导致os.path.join路径异常。解决在循环里加日志把跳过的文件夹打出来else: print(f跳过空目录: {case_dir})另外检查文件夹名有没有前后空格Windows 下os.listdir会保留空格os.path.isdir可能判断失败。3.5 模板里的页眉页脚在输出文档里丢了现象模板有页眉页脚生成的文档页眉页脚是空的。原因Document(template_path)会保留页眉页脚但如果模板的页眉页脚是“链接到前一节”且第一节被修改过可能出现异常。更常见的原因是模板本身是用旧版 Word 保存的python-docx 读取时 section 信息不完整。解决确认模板是 .docx 格式不是 .doc 改后缀用 Word 重新另存为 .docx。如果还不行在脚本里显式复制 sectionfrom copy import deepcopy for section in doc.sections: for hf in [section.header, section.footer]: for p in hf.paragraphs: pass # 页眉页脚内容已在模板中通常不需要额外操作这个问题的根因多半在模板不在脚本换一个干净的 .docx 模板通常就好了。4. 进阶技巧用配置表驱动不同模板和字段映射脚本包默认是“一个模板对应一种数据布局”但实际办公场景里经常是“同一批数据要套不同模板”或者“不同文件夹的表格列顺序不一样”。这时候硬编码就不够了我一般会加一层 YAML 配置# config.yaml templates: - name: inspection template: templates/inspection.docx image_width_cm: 10 table_style: Table Grid column_map: [0, 2, 1] # 把 CSV 的第0/2/1列映射到表格第0/1/2列 - name: summary template: templates/summary.docx image_width_cm: 14 table_style: Light Shading column_map: null # null 表示不重排读取配置的代码import yaml def load_config(path): with open(path, encodingutf-8) as f: return yaml.safe_load(f) def apply_column_map(row, mapping): if mapping is None: return row return [row[i] for i in mapping]这样改模板和列映射不用动 Python 代码交给办公岗同事改 YAML 就行。column_map的索引从 0 开始写的时候注意 CSV 里有没有序号列有的话要跳过。另一个进阶用法是给图片加统一水印。python-docx 本身不支持水印但可以在插入图片前用 PIL 把水印烧进图片from PIL import Image, ImageDraw def add_watermark(src, dst, text内部资料): img Image.open(src).convert(RGBA) overlay Image.new(RGBA, img.size, (0, 0, 0, 0)) draw ImageDraw.Draw(overlay) draw.text((10, 10), text, fill(255, 0, 0, 128)) out Image.alpha_composite(img, overlay).convert(RGB) out.save(dst, quality85)这个操作会重新编码图片画质有轻微损失如果原图是扫描件建议 quality 设 95 以上。验证方法上我习惯跑完脚本后随机抽三份文档打开检查图片顺序、表格边框、字体三处。批量场景下肉眼全检不现实但抽检能拦住大部分低级错误。另外可以在脚本末尾加一句统计print(f生成 {len(os.listdir(out_dir))} 份输入 {len(os.listdir(data_root))} 份)数字对不上就回头查 3.4 那条。从那以后我每次改完脚本都强制先拿两份样本数据跑一遍确认输出没问题再上全量。批量操作最怕的就是跑完一百份才发现模板选错了重跑的时间成本比验证高得多。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。