Python自动化办公实战:Excel、Word、PDF、PPT批量处理与避坑指南
简介:这份Python自动化办公教程面向希望提升日常办公效率的职场人士与编程初学者,以Python为工具解决表格处理、文档生成、邮件收发等重复性事务。教程从基础语法讲起,逐步覆盖Excel读写与图表制作、Word与PDF文档操作、PPT快速入门、邮件收发解析、数据库增删改查、网络爬虫与请求解析,以及文件、进程、线程等系统级操作,并配有实操案例与章节习题,便于边学边练。资源包共1个docx文件,约1.84MB,内容按章节组织,目录清晰,涵盖openpyxl、python-docx、python-pptx等常用库的用法与案例,适合作为系统学习与查阅的手册。目前已有1146人学习,说明其内容对办公自动化入门与进阶具有实际参考价值,读者可据此搭建完整的自动化办公技能框架。
1. 从一份目录说起:Python 自动化办公教程到底能解决什么
如果你每天的工作里有一半时间在 Excel 里复制粘贴、在 Word 里改模板、在 PPT 里对齐图表,那这份 Python 自动化办公教程就是冲着你来的。它不讲 Python 基础语法,直接从 openpyxl 操作 Excel 讲起,一路覆盖 python-docx 处理 Word、PDF 读写合并拆分、python-pptx 生成 PPT 图表,最后落到工资条生成、加班统计、查重、身份证信息提取这类真实办公场景。整份教程按三章组织,配套 50 多个编号示例,每个示例对应一个可运行的小脚本。适合两类人:一类是会用 Python 写循环和函数、但没系统操作过 Office 文件的开发者;另一类是被重复报表折磨、想用脚本替代手工的办公岗。它不教你从零学编程,但能让你在半天内跑通第一个自动化脚本。
2. openpyxl 三对象模型:把 Excel 读写拆成 Workbook、Worksheet、Cell
openpyxl 是这份教程的第一块地基,也是后面所有 Excel 案例的依赖。它的设计思路很清晰:一个 Excel 文件对应一个 Workbook,一个工作表对应一个 Worksheet,一个格子对应一个 Cell。三层对象各管各的,读数据从 Cell 往上拿,写数据从 Workbook 往下落。理解这个层级关系,后面调iter_rows()还是ws['A1']就不会犯迷糊。
2.1 安装与 Workbook 的创建、加载、保存
安装只有一条命令,但要注意 openpyxl 只认.xlsx,老式.xls得另找库。教程里明确写了这一点,实际项目里如果拿到的是.xls,常见做法是先手工另存为.xlsx,或者用 pandas 中转一次。
pip install openpyxl装完之后,创建和加载是两条不同的路径:新建用Workbook(),打开已有文件用load_workbook()。这里有个新手最容易翻车的地方——load_workbook()只是把文件读进内存,你对单元格做的任何修改都不会自动回写,必须显式调用save()。
import openpyxl # 新建一个工作簿,默认带一个名为 Sheet 的工作表 wb_new = openpyxl.Workbook() # 加载本地已有工作簿,filename 是必传参数 wb = openpyxl.load_workbook("excelTest.xlsx") # 所有修改完成后必须保存,否则原文件不变 wb.save("excelTest_out.xlsx")参数上,load_workbook()除了filename,常用的还有read_only=True(大文件只读模式,省内存)和data_only=True(读公式的计算结果而不是公式本身)。这两个参数在批量处理上千行报表时很关键,后面避坑章节会展开。
2.2 Worksheet 的获取、创建与行列访问
拿到 Workbook 之后,下一步是定位工作表。教程里给了三种方式:wb.active拿当前激活表,wb["Sheet1"]按名字拿,wb.sheetnames列出所有表名。创建新表用create_sheet(),删除用remove()。
import openpyxl wb = openpyxl.load_workbook("excelTest.xlsx") # 方式一:获取当前激活的工作表 ws = wb.active # 方式二:按名称获取 ws2 = wb["Sheet2"] # 方式三:列出所有表名,再遍历 print(wb.sheetnames) # ['Sheet1', 'Sheet2'] for sheet in wb: print(sheet.title) # 创建新工作表,第二个参数 index 控制插入位置 my_sheet = wb.create_sheet("mySheet", index=0)Worksheet 对象上最常用的属性是max_row、max_column、dimensions,它们告诉你“有数据的区域”边界。注意dimensions返回的是类似A1:D20的字符串,而max_row是整数,写循环时用后者更直接。访问整行整列可以用ws['C']拿 C 列、ws[6]拿第 6 行,切片ws[2:4]拿第 2 到第 4 行。批量读取推荐iter_rows(),它支持min_row、max_row、min_col、max_col四个参数,比手动拼坐标稳。
# 逐行读取,限定行范围和列范围,避免读到空行 for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=1, max_col=5): for cell in row: print(cell.value, end="\t") print()这段代码的逻辑是:从第 2 行开始(跳过表头),读到最大行,只取前 5 列。参数min_row=2是血泪经验——表头行通常不参与数据处理,从第 1 行开始读会把列名当数据算进去。
2.3 Cell 的坐标、取值与写入的三种姿势
Cell 对象身上就四个常用属性:row、column、value、coordinate。coordinate返回A1这种字符串坐标,row和column返回数字。写入数据教程给了三种方式:ws['A1'] = 值直接赋值、ws.cell(row=1, column=1, value=值)按坐标写、ws.append([...])按行追加。
import openpyxl wb = openpyxl.Workbook() ws = wb.active # 方式一:坐标赋值 ws["A1"] = "姓名" ws["B1"] = "部门" # 方式二:cell() 方法,适合循环里按行列号写 for i in range(2, 6): ws.cell(row=i, column=1, value=f"员工{i-1}") ws.cell(row=i, column=2, value="技术部") # 方式三:append() 整行追加,适合列表数据 ws.append(["张三", "财务部"]) ws.append(["李四", "市场部"]) wb.save("write_test.xlsx")三种方式没有优劣,看场景:表头固定用坐标赋值最直观,循环生成用cell()最灵活,从列表或数据库结果批量落盘用append()最省事。append()有个细节——它总是追加到当前最大行的下一行,不会覆盖已有数据,所以别在同一个表上反复 append 同一批数据,否则会翻倍。
3. 样式、公式与图表:让脚本产出的 Excel 能直接交差
只会读写值的脚本,产出的表格是“能用但没法看”。教程在读写之后专门讲了样式、公式、行高列宽、合并拆分和图表,这部分才是让自动化产物能直接发给同事的关键。很多人卡在这一步:数据对了,但格式全丢,最后还是得手工调。
3.1 字体、对齐与行高列宽的设置
openpyxl 的样式通过Font、Alignment、Border等类组合实现,改完赋值给 Cell 的对应属性。教程示例里改字体样式的写法是标准套路:先 import 样式类,实例化,再赋给cell.font。
import openpyxl from openpyxl.styles import Font, Alignment wb = openpyxl.load_workbook("excelTest.xlsx") ws = wb.active # 设置 A1 单元格字体:加粗、字号 12、颜色红色 ws["A1"].font = Font(name="微软雅黑", bold=True, size=12, color="FF0000") # 设置居中对齐 ws["A1"].alignment = Alignment(horizontal="center", vertical="center") # 设置行高和列宽,单位分别是磅和字符宽度 ws.row_dimensions[1].height = 30 ws.column_dimensions["A"].width = 20 wb.save("style_test.xlsx")参数说明:Font的color用十六进制 RGB 字符串,bold是布尔值;Alignment的horizontal可选center、left、right,vertical可选center、top、bottom。行高单位是磅(point),列宽单位是字符数,这两个数值调起来有点玄学,建议先手工在 Excel 里调好,记下数值再写进脚本。
3.2 公式写入与合并、拆分单元格
公式写入很直接,把公式字符串赋给value即可,openpyxl 不会计算公式,只负责把公式存进去,打开 Excel 时才由 Excel 引擎算。合并单元格用merge_cells(),拆分用unmerge_cells(),参数都是A1:D1这种范围字符串。
import openpyxl wb = openpyxl.Workbook() ws = wb.active # 写入公式,求和 A1 到 A3 ws["A1"] = 10 ws["A2"] = 20 ws["A3"] = 30 ws["A4"] = "=SUM(A1:A3)" # 合并 A1 到 D1,合并后只有左上角单元格保留值 ws.merge_cells("A1:D1") ws["A1"] = "合并后的标题" # 拆分回去 ws.unmerge_cells("A1:D1") wb.save("formula_test.xlsx")这里有个必须记住的边界:合并单元格后,只有左上角那个格子的值有效,其他格子的值会被丢弃。所以合并前先把值写到左上角,别写到中间格。另外,用data_only=True加载时,公式格读到的是上次 Excel 保存时缓存的计算结果,如果文件从没被 Excel 打开过,读出来是None,这个坑后面单独说。
3.3 饼图、条形图、散点图的生成
图表是教程里比较出彩的部分,openpyxl 支持饼图、条形图、气泡图、散点图。生成图表的流程固定:准备数据区域 → 创建图表对象 → 设置数据引用和分类引用 →add_chart()挂到工作表上。
import openpyxl from openpyxl.chart import PieChart, Reference wb = openpyxl.Workbook() ws = wb.active # 准备数据:A 列是分类,B 列是数值 data = [["部门", "人数"], ["技术", 30], ["市场", 20], ["财务", 10]] for row in data: ws.append(row) # 创建饼图对象 pie = PieChart() pie.title = "部门人数占比" # 数据引用:B2 到 B4;分类引用:A2 到 A4 data_ref = Reference(ws, min_col=2, min_row=2, max_row=4) cats_ref = Reference(ws, min_col=1, min_row=2, max_row=4) pie.add_data(data_ref, titles_from_data=False) pie.set_categories(cats_ref) # 挂到工作表 E2 位置 ws.add_chart(pie, "E2") wb.save("chart_test.xlsx")Reference的三个参数min_col、min_row、max_row决定了图表取哪块数据,titles_from_data控制是否把第一行当图例标题。条形图和散点图的写法类似,只是换图表类。图表这块的常见翻车是数据引用范围写错,导致图里多一行表头或少一行数据,建议生成后打开文件肉眼核对一遍。
4. Word、PDF 与 PPT:跨格式文档的批量处理链路
Excel 之外,教程用第二章和第三章覆盖了 Word、PDF、PPT。这三块的共同点是“模板化生成”——把固定格式的文档当模板,用脚本填数据、批量产出。实际办公里最常见的需求就是批量生成通知书、证明、报告,教程里的违章罚单通知书和复工证明两个案例就是干这个的。
4.1 python-docx 的段落、表格与图片插入
python-docx 的核心对象是Document,段落是Paragraph,表格是Table。新建文档用Document(),打开已有文档用Document("xxx.docx")。添加标题、段落、列表、引用、图片、表格都有对应方法。
from docx import Document from docx.shared import Pt doc = Document() # 添加标题,level 控制层级 doc.add_heading("月度工作报告", level=1) # 添加段落并设置字体 p = doc.add_paragraph("本月完成自动化脚本 12 个,") run = p.add_run("效率提升明显。") run.bold = True run.font.size = Pt(12) # 添加表格,3 行 2 列 table = doc.add_table(rows=3, cols=2) table.cell(0, 0).text = "项目" table.cell(0, 1).text = "状态" table.cell(1, 0).text = "报表自动化" table.cell(1, 1).text = "已完成" # 添加图片,指定宽度 doc.add_picture("chart.png", width=Pt(300)) doc.save("report.docx")参数上,add_heading()的level从 1 到 9,对应标题 1 到标题 9;add_picture()的width和height用Pt或Cm单位,只给一个另一个会等比缩放。读取 Word 用doc.paragraphs遍历段落、doc.tables遍历表格,教程示例 2-9 就是干这个的。
4.2 PDF 的读取、合并、拆分与加密
PDF 部分教程用的是 PyPDF2 系(新版叫 pypdf),覆盖读取内容、合并、拆分、加密。合并和拆分是批量处理里最高频的操作,比如把多个部门报表合成一个总文件,或者把一个大 PDF 按页拆开分发。
from pypdf import PdfReader, PdfWriter # 合并两个 PDF writer = PdfWriter() for f in ["part1.pdf", "part2.pdf"]: reader = PdfReader(f) for page in reader.pages: writer.add_page(page) writer.write("merged.pdf") # 拆分:取第一个文件的前 2 页单独存 reader = PdfReader("merged.pdf") writer2 = PdfWriter() for page in reader.pages[:2]: writer2.add_page(page) writer2.write("split_part.pdf") # 加密:设置用户密码 writer2.encrypt("user_pwd") writer2.write("encrypted.pdf")逻辑说明:PdfReader读、PdfWriter写,add_page()逐页搬运。拆分靠切片pages[:2]控制页范围,加密用encrypt()传密码字符串。注意加密要在write()之前调用,顺序反了密码不生效。Word 转 PDF 教程里也提了,但 python-docx 本身不支持导出 PDF,常见做法是调系统里的转换工具或另找库,这一步别指望纯 python-docx 搞定。
4.3 python-pptx 的幻灯片、占位符与图表
PPT 自动化的核心是“按模板填内容”。python-pptx 里Presentation是演示文稿,slide是单页,页上的文本框、图片、表格都通过占位符(placeholder)或add_*方法添加。
from pptx import Presentation from pptx.util import Inches prs = Presentation() # 用第 1 种版式(标题幻灯片)新建一页 slide = prs.slides.add_slide(prs.slide_layouts[1]) # 通过占位符填标题和正文 slide.shapes.title.text = "季度总结" slide.placeholders[1].text = "自动化办公落地情况" # 指定位置添加文本框 txBox = slide.shapes.add_textbox(Inches(1), Inches(2), Inches(4), Inches(1)) txBox.text_frame.text = "数据来源:内部报表" prs.save("demo.pptx")参数说明:slide_layouts[1]是版式索引,不同模板索引不同,用之前先打印prs.slide_layouts看一遍;add_textbox()四个参数是左、上、宽、高,单位用Inches或Cm。教程第三章还讲了流程图、条形图、折线图、饼图的插入,图表部分和 Excel 类似,都是先建图表对象再挂到 slide 上。
5. 避坑与排查:五个让脚本跑不通的真实原因
教程示例都是理想数据,实际跑起来翻车点不少。下面五条是我在类似项目里反复遇到的,按“现象 → 原因 → 解决”整理,照着排查能省不少时间。
5.1 保存后原文件没变
现象:脚本跑完没报错,打开原 Excel 发现数据没更新。原因:load_workbook()读进内存后,所有修改都在内存里,没调save()或者save()存到了另一个文件名。解决:确认wb.save()的路径和原文件一致,或者明确知道自己在存副本。另外read_only=True模式下不能保存,只读模式只能读。
5.2 公式格读出来是 None
现象:用data_only=True读公式单元格,拿到None。原因:openpyxl 不计算公式,data_only读的是 Excel 上次保存时缓存的结果,如果文件是脚本生成的、从没被 Excel 打开保存过,缓存不存在。解决:要么先用 Excel 打开保存一次,要么在脚本里自己算,别依赖data_only读脚本刚写的公式。
5.3 合并单元格后数据丢失
现象:合并A1:D1后,原来 B1、C1、D1 的值没了。原因:合并只保留左上角单元格的值,其余被清空。解决:合并前把要保留的值写到左上角,或者先合并再赋值。拆分unmerge_cells()不会恢复被丢弃的值,这点没有后悔药。
5.4 大文件读取内存爆掉
现象:处理几万行的 Excel 时脚本卡死或内存溢出。原因:默认load_workbook()把整个文件加载进内存,行数一多就撑不住。解决:用read_only=True只读模式配合iter_rows()流式读取,处理完及时释放;写大文件用write_only=True模式。这两个模式有功能限制,比如只读模式不能改样式,按需取舍。
5.5 PDF 加密后打不开
现象:加密后的 PDF 输入密码提示错误。原因:encrypt()调用顺序在write()之后,或者密码传了空字符串。解决:确保writer.encrypt("密码")在writer.write("文件.pdf")之前执行,密码别传空。另外不同 PDF 阅读器对加密算法兼容性有差异,产出的文件最好用两三个阅读器各开一次验证。
6. 从示例到产线:把工资条脚本改造成可复用工具
教程里的示例 1-24 生成工资条、1-25 统计加班时间、1-26 快速查重、1-27 提取身份证信息,都是单文件脚本。真正要天天用,得把它们改成带参数、能批量、有日志的小工具。我一般会做三件事:把文件路径和表头行号抽成命令行参数,把“读 → 处理 → 写”拆成三个函数,加一层异常捕获把出错行号打出来。
import argparse import openpyxl def read_rows(path, header_row=1): """读取数据行,跳过表头,返回字典列表""" wb = openpyxl.load_workbook(path, data_only=True) ws = wb.active headers = [c.value for c in ws[header_row]] rows = [] for row in ws.iter_rows(min_row=header_row + 1, values_only=True): if all(v is None for v in row): continue # 跳过空行 rows.append(dict(zip(headers, row))) return rows def write_rows(path, rows, headers): """把字典列表写回新文件""" wb = openpyxl.Workbook() ws = wb.active ws.append(headers) for r in rows: ws.append([r.get(h) for h in headers]) wb.save(path) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--input", required=True) parser.add_argument("--output", required=True) parser.add_argument("--header-row", type=int, default=1) args = parser.parse_args() try: data = read_rows(args.input, args.header_row) # 这里替换成具体业务逻辑,比如按部门过滤、算加班时长 write_rows(args.output, data, list(data[0].keys()) if data else []) print(f"处理完成,共 {len(data)} 行") except Exception as e: print(f"处理失败:{e}")这段代码的价值在于把“路径”和“表头位置”从硬编码里解放出来,换一个文件不用改代码。values_only=True让iter_rows()直接返回值的元组,省去.value的层层访问;all(v is None for v in row)过滤空行,避免把 Excel 底部的空白行当数据。异常捕获里打印e而不是吞掉,出错时至少知道是哪一步挂的。
验证方法上,我习惯拿一份手工算过结果的小样本(比如 10 行)先跑一遍,把脚本输出和手工结果逐格对比,确认无误再上全量。全量跑完再抽查首行、末行和中间随机三行,这三处最容易暴露边界问题——首行看表头有没有被误当数据,末行看空行有没有被算进去,中间行看类型转换有没有把数字变成字符串。
从那以后我每次写这类脚本,都强制先跑小样本再上全量,宁可多花十分钟核对,也不愿对着几万行错误数据返工。希望帮到你。
本文还有配套的精品资源,点击获取