ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python自动化办公教程:从周报整理到定时任务,掌握Excel、Word、PDF批量处理

Python自动化办公教程:从周报整理到定时任务,掌握Excel、Word、PDF批量处理

简介:这是一份面向职场办公人员与编程初学者的Python自动化办公课程资源,围绕用代码替代重复手工操作这一目标展开,技能层级由入门语法逐步过渡到实战应用。内容覆盖Python基础语法、Excel读写与图表、Word与PDF文档处理、PPT制作、邮件收发、数据库增删改查、网络爬虫及系统文件与进程线程管理等模块,并配有习题与实操案例,便于边学边练。资源包为1个docx文档,约1.84MB,以图文教程形式系统梳理各章节知识点,目录按模块划分清晰,方便按需检索。目前已有1141人学习,适合希望提升日常办公效率、减少重复劳动,或想系统入门Python自动化方向的读者参考使用。

1. 从手动整理 300 份周报到一键生成:python自动化办公教程到底在教什么

每周一早上,某公司的运营岗 A 同学都要做同一件事:打开邮箱,把 12 个部门发来的周报附件逐个下载,统一改名成「部门-日期」格式,再把里面的关键数据摘出来填进汇总表。这套动作她做了两年,平均耗时 3 小时,偶尔漏掉一个附件还得返工。python自动化办公教程要解决的,就是这类「重复、有规律、但手动做又累又容易错」的桌面任务。

它不是什么高深技术,核心就三件事:用 Python 读写文件、操控 Excel/Word/PDF、批量处理目录和邮件。适合每天被表格和文档淹没的行政、运营、财务、测试岗,也适合想从脚本开始接触编程的新手。学完能落地的标志很明确——你能把一件每周重复 2 小时以上的事,压缩成双击一次、10 秒跑完的脚本。这一章先把边界划清楚,后面几章再拆具体怎么做、参数怎么调、哪里会翻车。

2. 环境与工具链:把 python自动化办公教程的第一块砖铺稳

很多人卡在第一步不是不会写代码,而是环境装完就报错。这一章把最小可用环境搭起来,顺带说清每个库为什么选它。

2.1 解释器、编辑器与虚拟环境的三件套

Python 版本建议 3.10 以上,原因是 3.10 起对类型提示和错误信息做了明显优化,调试脚本时能少猜很多。编辑器用 VS Code 或 PyCharm 社区版都行,前者轻、后者补全强,新手我更推荐 VS Code,装个 Python 扩展就能跑。

虚拟环境是必须的,不是可选项。系统级装库一旦版本冲突,排查起来就是黑匣子。用 venv 建一个独立环境:

# 创建名为 office 的虚拟环境 python -m venv office # Windows 激活 office\Scripts\activate # macOS / Linux 激活 source office/bin/activate # 激活后命令行前缀会出现 (office),说明生效

激活后所有 pip 安装都只影响这个环境,删掉文件夹就等于卸载干净。这一步的收益在后期:当你同时维护三个脚本、依赖不同版本的库时,不会互相打架。

2.2 四个核心库的选型与安装

自动化办公绕不开四类文件:Excel、Word、PDF、目录与邮件。对应库和选择理由如下:

任务类型推荐库选它的理由
Excel 读写openpyxl支持 .xlsx 读写、公式、样式,纯 Python 无外部依赖
Excel 数据分析pandas批量统计、透视、合并比手写循环快一个量级
Word 文档python-docx段落、表格、样式都能操作,API 直观
PDF 处理pypdf合并、拆分、提取文本稳定,纯 Python
目录与文件pathlib(标准库)跨平台路径处理,比 os.path 更清爽
邮件收发imaplib / smtplib(标准库)无需额外依赖,够用

安装命令一次装齐:

pip install openpyxl pandas python-docx pypdf

提示:pandas 在 Windows 上如果装得慢,是编译依赖的问题,直接装预编译 wheel 即可,不要手动编译。

2.3 用 pathlib 统一管理文件路径

新手最容易翻车的地方是路径拼接。Windows 用反斜杠、macOS 用正斜杠,硬编码字符串换台机器就崩。pathlib 把路径当对象处理,跨平台自动适配:

from pathlib import Path # 当前脚本所在目录 base = Path(__file__).parent # 拼接子目录,/ 运算符由 pathlib 重载 data_dir = base / "data" # 遍历目录下所有 xlsx 文件 for f in data_dir.glob("*.xlsx"): print(f.name, f.stat().st_size) # 文件名与字节大小

glob("*.xlsx")只匹配当前层,要递归子目录用rglob("*.xlsx")。f.stat().st_size返回字节数,用来做文件大小过滤很实用,比如跳过 0 字节的空文件。把路径统一成 Path 对象后,后面所有读写都传它,代码可移植性直接上一个台阶。

3. Excel 批量处理:python自动化办公教程里最高频的落地场景

Excel 是办公自动化的主战场。这一章从单文件读写讲到多文件合并,再到格式保留,每一步都能直接抄。

3.1 openpyxl 读写单元格与保留格式

openpyxl 的优势是能保留原文件的样式、公式和合并单元格。读一个工作簿:

from openpyxl import load_workbook # data_only=True 读取公式的计算结果而非公式本身 wb = load_workbook("report.xlsx", data_only=True) ws = wb.active # 默认第一个工作表 # 按行遍历,values_only 直接拿值不拿单元格对象 for row in ws.iter_rows(min_row=2, values_only=True): name, amount, date = row[0], row[1], row[2] print(name, amount, date)

min_row=2跳过表头,values_only=True返回元组而非 Cell 对象,速度快很多。写回时如果要保留原格式,用load_workbook打开再改,不要用Workbook()新建:

wb = load_workbook("template.xlsx") ws = wb["汇总"] ws["B2"] = 12345 # 写入值 ws["B2"].number_format = "#,##0.00" # 设置千分位格式 wb.save("template_filled.xlsx")

number_format的取值遵循 Excel 格式代码,#,##0.00是千分位加两位小数。这一步的坑在于:如果原单元格是合并单元格,只有左上角那个格能写值,写其他格会报错。

3.2 pandas 合并多表与去重统计

当目录下有几十个结构相同的表,pandas 的concat比手写循环快得多:

import pandas as pd from pathlib import Path files = Path("data").glob("*.xlsx") # 列表推导读入所有表,ignore_index 重排索引 df = pd.concat([pd.read_excel(f) for f in files], ignore_index=True) # 按「部门」分组求金额合计 summary = df.groupby("部门", as_index=False)["金额"].sum() # 按「订单号」去重,保留第一条 dedup = df.drop_duplicates(subset="订单号", keep="first") summary.to_excel("汇总.xlsx", index=False)

ignore_index=True很关键,不加的话每个文件的索引会重复,后续按索引取值会错乱。groupby的as_index=False让分组键保留为普通列,导出时不会变成索引。drop_duplicates的keep参数可选first/last/False,False表示只要重复就全删。

3.3 一个可复用的「多表合并 + 汇总」脚本

把上面两步串起来,加异常处理和日志,就是一个能长期用的脚本:

import logging import pandas as pd from pathlib import Path logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") DATA = Path("data") OUT = Path("output") OUT.mkdir(exist_ok=True) frames = [] for f in DATA.glob("*.xlsx"): try: df = pd.read_excel(f) df["来源文件"] = f.name # 标记数据来源,便于追溯 frames.append(df) logging.info("已读入 %s,%d 行", f.name, len(df)) except Exception as e: logging.error("读取 %s 失败:%s", f.name, e) if frames: all_df = pd.concat(frames, ignore_index=True) result = all_df.groupby("部门", as_index=False)["金额"].sum() result.to_excel(OUT / "汇总.xlsx", index=False) logging.info("汇总完成,共 %d 行", len(result))

df["来源文件"] = f.name这行是血泪经验:合并后如果数据对不上,没有来源标记就得逐个文件翻。try/except保证单个文件损坏不会中断整个批次。OUT.mkdir(exist_ok=True)让输出目录不存在时自动创建,exist_ok=True避免重复运行时抛异常。

4. Word 与 PDF 自动化:python自动化办公教程的文档流水线

Excel 之外,文档类任务同样高频。这一章讲怎么批量生成 Word、怎么合并拆分 PDF。

4.1 python-docx 批量生成合同与报告

python-docx 的核心对象是 Document、Paragraph、Table。批量生成时,通常先做一个模板,再用数据替换占位符:

from docx import Document doc = Document("template.docx") # 遍历所有段落,替换占位符 for p in doc.paragraphs: if "{name}" in p.text: for run in p.runs: run.text = run.text.replace("{name}", "张三") doc.save("output/张三.docx")

这里有个细节:Word 里一段文字可能被拆成多个 run(比如你中途改过字体),直接改p.text是只读的,必须遍历p.runs逐个替换。如果占位符跨了 run,替换会失败,稳妥做法是先把整段文本读出来判断,再重建段落。

表格填充类似:

table = doc.tables[0] table.cell(1, 1).text = "2024-06-01" # 第2行第2列

cell(row, col)从 0 开始计数,第 0 行通常是表头。

4.2 pypdf 合并、拆分与提取文本

PDF 的常见需求是合并多个文件、按页拆分、提取文字做检索:

from pypdf import PdfReader, PdfWriter # 合并 writer = PdfWriter() for f in ["a.pdf", "b.pdf", "c.pdf"]: writer.append(f) writer.write("merged.pdf") writer.close() # 拆分:把第 1-3 页单独存一个文件 reader = PdfReader("merged.pdf") part = PdfWriter() for i in range(3): part.add_page(reader.pages[i]) part.write("part1.pdf") part.close() # 提取文本 text = "".join(page.extract_text() for page in reader.pages)

append直接接文件路径,比先读再写省内存。extract_text()对扫描件无效,扫描件是图片,需要 OCR,这是两套技术路线,别混。拆分时range(3)对应第 0、1、2 页,即文档的第 1 到第 3 页,索引从 0 开始这点要记牢。

4.3 文档批处理的目录约定与命名规范

批量处理最怕文件乱放。我一般约定三层结构:input/放原始文件,output/放结果,archive/放处理完的备份。命名统一用「日期_类型_序号」,比如20240601_合同_001.docx。这样脚本里用glob("*.docx")就能稳定匹配,排序也不会乱。

处理完自动归档:

import shutil from pathlib import Path src = Path("input") done = Path("archive") done.mkdir(exist_ok=True) for f in src.glob("*.docx"): shutil.move(str(f), done / f.name)

shutil.move跨磁盘也能用,比os.rename稳。归档前建议先确认输出文件已生成,否则原始文件移走了、结果没出来,就得从备份找,这种翻车我见过不止一次。

5. 避坑与排查:python自动化办公教程里没人明说的 5 个坑

这一章全是踩过的坑,按「现象 → 原因 → 解决」写,遇到问题直接对号入座。

5.1 文件被占用导致 PermissionError

现象:脚本报PermissionError: [Errno 13] Permission denied,明明路径没错。原因:目标 Excel 或 Word 文件正被 Office 打开,Windows 会锁文件。解决:处理前先确认文件已关闭,或在脚本里捕获异常并提示;批量场景下可以先复制一份到临时目录再操作,避开锁。

5.2 中文路径与编码乱码

现象:读取含中文的 CSV 时报UnicodeDecodeError,或写入后打开是乱码。原因:CSV 默认编码在不同系统不一致,Windows 常用 GBK,其他平台常用 UTF-8。解决:读写时显式指定encoding="utf-8-sig",utf-8-sig带 BOM,Excel 打开不乱码:

df = pd.read_csv("data.csv", encoding="utf-8-sig") df.to_csv("out.csv", index=False, encoding="utf-8-sig")

5.3 日期格式被 pandas 读成字符串

现象:合并后按日期排序,结果顺序是乱的。原因:Excel 里日期存的是序列号,pandas 读进来可能是字符串或数字。解决:读入后用pd.to_datetime显式转换,errors="coerce"把无法解析的置为 NaT,便于排查:

df["日期"] = pd.to_datetime(df["日期"], errors="coerce")

5.4 大文件一次性读入内存爆掉

现象:处理几万行的表时脚本卡死或内存溢出。原因:read_excel默认全量加载。解决:用chunksize分块读,或先过滤列再读:

for chunk in pd.read_excel("big.xlsx", chunksize=5000): process(chunk) # 每 5000 行处理一次

5.5 脚本在别人电脑上跑不起来

现象:自己机器正常,同事一跑就报模块找不到。原因:依赖没固化,或用了绝对路径。解决:用pip freeze > requirements.txt导出依赖,对方pip install -r requirements.txt即可;路径全部用Path(__file__).parent相对定位,不写死盘符。

6. 从脚本到定时任务:让 python自动化办公教程真正省下时间

脚本写完只是半成品,能自动跑才算闭环。这一章讲怎么把脚本变成定时任务,以及怎么验证它真的在干活。

6.1 用 schedule 做进程内定时

最简单的定时方式是在脚本里挂一个调度器,适合常驻运行的场景:

import schedule import time def job(): print("开始执行汇总...") # 这里调用你的汇总函数 schedule.every().day.at("08:30").do(job) schedule.every().monday.at("09:00").do(job) while True: schedule.run_pending() time.sleep(30) # 每 30 秒检查一次

every().day.at("08:30")是每天 8:30 执行,every().monday是每周一。time.sleep(30)控制检查频率,太短浪费 CPU,太长会错过精确时间点,30 到 60 秒是常见取值。这个方案的局限是脚本必须一直开着,关机就停。

6.2 系统级定时:Windows 任务计划与 cron

要脱离终端常驻,用系统自带调度。Windows 用「任务计划程序」,创建基本任务,触发器选每天,操作选「启动程序」,程序填 python.exe 路径,参数填脚本路径。macOS 和 Linux 用 crontab:

# 编辑当前用户的定时任务 crontab -e # 每天 8:30 执行,日志追加到 run.log 30 8 * * * /usr/bin/python3 /path/to/script.py >> /path/to/run.log 2>&1

五个星号依次是分、时、日、月、周。>> run.log 2>&1把标准输出和错误都写进日志,出问题时有据可查。注意 cron 的环境变量和登录 shell 不同,脚本里尽量用绝对路径,或在 crontab 顶部显式设置 PATH。

6.3 验证脚本真的在跑:日志与退出码

定时任务最坑的是「以为在跑,其实早挂了」。验证手段有三个:一是看日志文件有没有新内容,二是看输出目录的文件修改时间,三是看退出码。在脚本末尾加:

import sys import logging logging.basicConfig( filename="run.log", level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s" ) try: main() logging.info("任务完成") sys.exit(0) # 0 表示成功 except Exception as e: logging.exception("任务失败") sys.exit(1) # 非 0 表示失败,调度器可据此告警

sys.exit(0)和sys.exit(1)的区别在系统级调度里很重要,任务计划程序可以根据退出码判断成败并触发通知。日志用logging.exception会连堆栈一起记,比print强太多。

6.4 一个我坚持了三年的习惯

我自己的习惯是:任何自动化脚本上线前,先手动跑三遍,确认输出一致;再故意制造一次异常(比如删掉一个输入文件),看它是否优雅报错而不是静默跳过。静默失败是自动化最大的敌人——它不报错,你以为一切正常,等到月底对账才发现少了几百条数据。脚本可以写得丑,但日志和退出码不能省。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进