这次我们再回到一个很常见的需求:用 Python 处理 Excel 表格。很多做办公自动化的人,一开始想到的都是 Excel 自带的 VBA 宏。VBA 在单文件、单表格里确实够用,但一旦遇到“几十个 Excel 要合并”“每天从系统导出报表后清洗一遍”“要把某个表格里的字段匹配到另一张表”这类任务,VBA 的开发和维护成本都会明显上升。Python 的优势在于:不用在 Excel 界面里操作,一个脚本可以反复执行,也能和其他系统、数据库、定时任务直接对接。本文就是一篇面向入门者的 Excel 表格处理基础教程,适合刚接触 Python 办公自动化的读者,也适合想从 VBA 切到 Python 的办公人员。
本次基础篇会演示几个非常高频的操作:读取工作表、写入新工作簿、设置单元格样式、把空白单元格自动填充上一行内容、按关键字匹配两张表的数据、批量合并多个 xlsx 文件、按条件拆分数据,以及根据 Excel 清单批量重命名 Word 文件。这些场景基本覆盖了日常办公里“反复手工复制粘贴”的典型痛点。环境方面,这个主题没有 GPU、显存之类的门槛,普通办公电脑能安装 Python 就能运行,主要消耗的是 CPU 和内存。下面直接进入正题。
1. Python 操作 Excel 的核心能力速览
先把整体能力和技术选型表格放在前面,方便快速判断自己应该学哪种写法。
| 项目 | 说明 |
|---|---|
| 操作对象 | Excel 工作簿、工作表、单元格、区域、图表、公式 |
| 常见文件格式 | .xlsx(新版 Excel)、.xls(旧版,需额外处理) |
| 推荐技术方案 | openpyxl、pandas、xlsxwriter |
| 运行环境 | Python 3.8 及以上版本均可,推荐 3.10+ |
| 硬件要求 | 无需 GPU,普通 CPU + 8GB 内存可满足大部分场景 |
| 主要使用方式 | 命令行运行脚本、PyCharm / VS Code 运行、定时任务调用 |
| 是否支持批量任务 | 支持,循环读取多个文件并输出即可 |
| 是否提供接口 API | 基础操作不涉及;可封装成函数供 Web 服务或定时脚本调用 |
| 适合场景 | 表格合并拆分、数据匹配、格式整理、批量生成报表、重复性办公操作 |
| 学习难度 | 低,掌握读取、遍历、写入三步即可开始 |
不同第三方库的侧重点也不同。建议初学者优先掌握 openpyxl 和 pandas 两套方案:
| 方案 | 读取 .xlsx | 写入 .xlsx | 批量处理 | 数据分析 | 适用场景 |
|---|---|---|---|---|---|
| openpyxl | 支持 | 支持 | 支持 | 一般 | 单元格级操作、格式设置、公式写入 |
| pandas | 支持 | 支持 | 很好 | 很强 | 数据清洗、统计、合并、匹配 |
| xlsxwriter | 不支持读取 | 支持 | 支持 | 一般 | 只写不读、生成带图表的报表 |
| xlrd | 只读 .xls | 不支持 | 一般 | 一般 | 处理旧版 .xls 文件 |
2. 适用场景与使用边界
2.1 这个工具适合谁
Python 处理 Excel 的典型用户有三类。第一类是运营、人事、财务岗位的办公人员,他们经常需要处理系统导出的报表,希望减少重复劳动。第二类是 Python 开发者和自动化运维人员,他们需要把表格数据接入到脚本或业务系统里。第三类是数据相关岗位,例如数据分析师,他们需要快速做数据合并、筛选、统计和导出。
从熟练度来看,只要掌握了“用 Python 读取一个 xlsx 文件、循环每一行、把结果写入新文件”这三步,就已经能解决很多基础表格需求。这也是后续做数据清洗、图表自动化、日报自动生成、批量文件重命名等复杂任务的前置条件。
2.2 不适合什么场景
不过也要说清楚边界。如果只是临时查看一个表格,直接打开 Excel 处理反而更快,没必要用脚本。如果需要生成带大量复杂交互功能的工作簿,例如用户需要在 Excel 里点击按钮做联动、切换视图、执行宏,Python 写完后仍然要保留用户交互,那么要考虑是否应该用 VBA 或者 Office 插件方案。另外,如果文件是非常老旧的 .xls 格式,openpyxl 无法直接处理,需要先转换格式或改用其他依赖库。
2.3 数据安全与合规边界
办公自动化处理的大多是工作数据,可能有客户名单、工资、合同金额等敏感信息。使用脚本时要注意:
- 不要随便把包含个人身份信息或商业机密的表格上传到在线转换工具。
- 本地脚本处理时,原始文件先备份,输出结果单独放目录。
- 如果涉及他人个人信息,应遵守公司数据管理制度和相关法律法规。
- 批量处理前先检查文件来源,防止带宏病毒或异常数据的文件进入流程。
- 如果脚本要共享给同事,注意隐藏敏感信息路径和内部数据。
3. 环境准备与前置条件
3.1 检查 Python 环境
在开始写 Excel 脚本前,先确认电脑上已经安装 Python。打开命令提示符(Windows)或终端(macOS / Linux),执行:
python --version如果显示Python 3.x.x,说明环境可用。如果提示找不到命令,需要先安装 Python。安装时记得勾选“Add Python to PATH”,这样后续在命令行里执行pip install会更省事。
建议使用 Python 3.10 及以上版本。Python 3.8 也能运行本教程的代码,但新版在性能、依赖兼容性上更好。
3.2 安装第三方依赖库
本文需要用到 openpyxl 和 pandas。pandas 读取 Excel 时底层依赖 openpyxl,所以两个库都要安装:
pip install openpyxl pandas如果网络较慢,可以指定使用国内镜像源:
pip install openpyxl pandas -i https://pypi.tuna.tsinghua.edu.cn/simple安装后,可以在 Python 里写一行代码确认:
import openpyxl import pandas print("openpyxl 版本:", openpyxl.__version__) print("pandas 版本:", pandas.__version__)能正常打印版本号,说明依赖安装完成。如果提示ModuleNotFoundError,说明对应库没有安装成功,需要回到上一步重新安装。
3.3 确认 Excel 文件格式
需要注意 .xlsx 和 .xls 的区别。.
- .xlsx 是 Excel 2007 之后的标准格式,openpyxl 和 pandas 都能直接读取。
- .xls 是 Excel 97-2003 的旧格式,openpyxl 不支持,pandas 需要额外依赖 xlrd。
对于旧 .xls 文件,最简单的处理方式是用 Excel 打开后另存为 .xlsx,或者在代码里用 pandas 指定引擎读取。基础篇里统一以 .xlsx 为例。
3.4 准备测试目录
为了不把原始数据和脚本混在一起,建议新建一个测试目录,例如:
excel-demo/ ├── 原始数据/ │ ├── 销售_1.xlsx │ └── 销售_2.xlsx ├── 输出结果/ └── 脚本/ ├── 01_读取表格.py └── 02_合并文件.py目录可以按自己习惯调整,但建议保持三块分离:原始数据不动,输出结果单独放置,脚本统一管理。这样做的好处是,批量处理时不会因为脚本输出文件覆盖原始文件而造成数据丢失。
4. 快速上手:读取 Excel 与写入新表
4.1 用 openpyxl 读取单元格
先准备一个测试文件演示表格.xlsx,内容为人员名单。然后用 openpyxl 读取第一个工作表和指定单元格:
from openpyxl import load_workbook # 读取工作簿 wb = load_workbook("原始数据/演示表格.xlsx") # 获取当前活动的工作表 ws = wb.active print("工作表名称:", ws.title) print("A1 单元格内容:", ws["A1"].value) print("B2 单元格内容:", ws["B2"].value)运行后可以看到工作表名称和单元格内容。这里load_workbook是读取工作簿的入口,函数参数支持文件路径。路径里带中文一般没有问题,但不建议把文件放在名称有特殊符号的目录下。
4.2 遍历工作表中的所有数据
实际办公中通常不是只看一个单元格,而是要遍历所有行。用iter_rows可以按行返回数据:
from openpyxl import load_workbook wb = load_workbook("原始数据/演示表格.xlsx") ws = wb.active for row in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=ws.max_column, values_only=True): print(row)values_only=True表示返回单元格的值,而不是单元格对象。这样每行会得到一个元组,例如("张三", "研发部", 12000)。后续要做汇总、筛选,可以先把这个元组转成列表,再按需求处理。
4.3 用 pandas 读取表格并进行统计
如果只是读取数据,openpyxl 完全够用。但要做数据统计和匹配,pandas 的 DataFrame 会方便很多:
import pandas as pd df = pd.read_excel("原始数据/销售明细.xlsx", sheet_name="1月") print(df.head()) print("总行数:", len(df)) print("列名:", list(df.columns)) print("销售额合计:", df["销售额"].sum())这里sheet_name可以传工作表名称,也可以传索引,例如sheet_name=0表示第一个工作表。head()默认显示前 5 行,用于快速确认数据是否读取正确。
如果读取 .xlsx 文件时遇到Excel file format cannot be determined或Missing optional dependency 'openpyxl'这类错误,通常是因为文件格式不一致,或者电脑里没有安装 openpyxl。解决办法是先把文件另存为标准 .xlsx 格式,再确认安装了 openpyxl。
4.4 写入新的 Excel 文件
写入一个基本的工作簿非常简单:
from openpyxl import Workbook wb = Workbook() ws = wb.active ws.title = "员工工资" ws.append(["姓名", "部门", "工资"]) ws.append(["张三", "研发部", 12000]) ws.append(["李四", "市场部", 10000]) ws.append(["王五", "财务部", 11000]) wb.save("输出结果/工资表.xlsx") print("文件已生成")append会把一行数据追加到工作表末尾。第一次写入时,表头会放在第 1 行,随后每调用一次append就新增一行。保存后可以用 Excel 打开输出结果/工资表.xlsx检查内容。
4.5 设置表头样式
如果希望输出文件更接近正式报表,可以给表头设置加粗、背景色和居中:
from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill wb = Workbook() ws = wb.active ws.title = "员工工资" headers = ["姓名", "部门", "工资"] rows = [ ["张三", "研发部", 12000], ["李四", "市场部", 10000], ] ws.append(headers) header_fill = PatternFill("solid", fgColor="4472C4") header_font = Font(bold=True, color="FFFFFF") center_alignment = Alignment(horizontal="center", vertical="center") for cell in ws[1]: cell.fill = header_fill cell.font = header_font cell.alignment = center_alignment for row in rows: ws.append(row) wb.save("输出结果/员工工资_带样式.xlsx")代码里先写入表头,再对第 1 行的每个单元格设置填充色和字体,然后写入数据。这类样式操作是 openpyxl 的强项,后续要加边框、列宽、单元格合并等,也都围绕cell对象展开。
4.6 写入公式
openpyxl 支持在单元格里写 Excel 公式,比如计算两列相乘:
from openpyxl import Workbook wb = Workbook() ws = wb.active ws["A1"] = 数量 ws["A1"] = 10 ws["B1"] = 20 ws["C1"] = "=A1*B1" wb.save("输出结果/带公式.xlsx")需要注意:openpyxl 只负责写入公式,并不会自己计算结果显示值。用 Excel 打开文件后,Excel 会重新计算并显示结果。如果脚本立刻用data_only=True读取 C1,得到的可能是None,因为文件还没有被 Excel 打开计算过。这是 openpyxl 的一个常见特点,后面读取公式单元格值时要特别注意。
5. 基础功能案例:覆盖办公中最高频的 6 类操作
这一部分从常见搜索需求里梳理出 6 个高频案例。每个案例都按“目标、输入、代码、结果判断”来组织,可以直接复制改路径使用。
5.1 让空白单元格自动填充上一行内容
这是一个非常常见的场景:原始表里为了可读性,同一组的部门名称只在第一行填写,后续行都是空值。做统计时这些空单元格会导致筛选和透视出错,所以需要把空值补成上一行同列的内容。
假设原始表结构如下:
| 姓名 | 部门 | 工资 |
|---|---|---|
| 张三 | 研发部 | 12000 |
| 李四 | 10000 | |
| 王五 | 11000 | |
| 赵六 | 市场部 | 9000 |
| 孙七 | 8500 |
可以看到部门列出现了合并单元格式的纵向省略。目标是从第 2 行开始扫描,遇到空值就补上“最近一次非空值”:
from openpyxl import load_workbook wb = load_workbook("原始数据/人员表.xlsx") ws = wb.active # 从第2行开始,因为第1行通常是表头 for col in range(1, ws.max_column + 1): last_value = None for row_index in range(2, ws.max_row + 1): cell = ws.cell(row=row_index, column=col) value = cell.value if value is None or str(value).strip() == "": # 当前单元格为空,用上一行同列的最近非空值填充 if last_value is not None: cell.value = last_value else: last_value = value wb.save("输出结果/人员表_已填充.xlsx") print("填充完成")运行前记得备份原始文件,因为这个脚本会覆盖保存到新路径,虽然不会改动原始数据,但错误的填充逻辑可能会覆盖有效值。判断成功的标准是:输出结果/人员表_已填充.xlsx中所有空部门都被填上了内容。
5.2 将多列内容合并到一起,并添加符号间隔
第二个高频需求是把“省份”“城市”“地区”等多列合成一列,中间用横线、逗号或竖线分隔。例如把“北京-研发部-张三”这种格式拼出来。
用 pandas 最简单:
import pandas as pd df = pd.read_excel("原始数据/人员表.xlsx") # 处理空值,避免出现字符串 "nan" df = df.fillna("") df["完整信息"] = df["城市"].astype(str) + "-" + df["部门"].astype(str) + "-" + df["姓名"].astype(str) df.to_excel("输出结果/人员表_合并列.xlsx", index=False)这里的核心逻辑是通过+拼接字符串。如果列里有数字,例如工号 1001,直接用astype(str)能避免数字和字符串拼接报错。分隔符可以根据需求改成、、,、|等。
如果你习惯用 openpyxl,也可以逐行拼接已有单元格内容后写入新列:
from openpyxl import load_workbook wb = load_workbook("原始数据/人员表.xlsx") ws = wb.active # 假设 G 列是新列 ws["G1"] = "完整信息" for row_index in range(2, ws.max_row + 1): city = ws.cell(row=row_index, column=4).value or "" department = ws.cell(row=row_index, column=2).value or "" name = ws.cell(row=row_index, column=1).value or "" ws.cell(row=row_index, column=7).value = f"{city}-{department}-{name}" wb.save("输出结果/人员表_合并列_openpyxl.xlsx")5.3 两张表按关键字匹配数据,类似 VLOOKUP
很多办公人员习惯用 VLOOKUP 函数在一张表里匹配另一张表的数据。Python 里对应操作是 pandas 的merge。
场景:工资表里有员工工号,但没有部门信息;员工基础表里有工号和部门。目标是把部门匹配到工资表上。
import pandas as pd salary_df = pd.read_excel("原始数据/工资表.xlsx") base_df = pd.read_excel("原始数据/员工基础表.xlsx") # 只保留需要匹配的列 base_subset = base_df[["工号", "部门"]] # 以工资表为主表,按工号匹配 result_df = salary_df.merge(base_subset, on="工号", how="left") result_df.to_excel("输出结果/工资表_补部门.xlsx", index=False) print("匹配完成,共", len(result_df), "行")参数说明:
on="工号"表示两边共用同一列名。how="left"表示保留左边表全部行,右边匹配不到的数据用 NaN 填充。- 如果两张表中关键字段名不一样,例如一边叫
工号,另一边叫员工编号,可以改用left_on="工号", right_on="员工编号"。
判断成功的标准是:查一下输出文件中原来为空或缺失的部门列是否被正确填充。如果有多条记录匹配上,数据量可能会变多,需要先检查原表是否有一对多关系。
5.4 批量合并多个 Excel 文件
这个场景在财务、运营汇总时非常常见,比如一个文件夹下有多个月份报表,想把它们合并成一个总表。
import pandas as pd from pathlib import Path folder = Path("原始数据/销售数据") output_path = "输出结果/销售合并.xlsx" frames = [] for file in folder.glob("*.xlsx"): # 防止读取到已经生成的输出文件 if file.name == output_path: continue print("正在读取:", file.name) df = pd.read_excel(file) frames.append(df) if frames: merged_df = pd.concat(frames, ignore_index=True) merged_df.to_excel(output_path, index=False) print("合并完成,总行数:", len(merged_df)) else: print("没有找到可合并的 Excel 文件")使用glob("*.xlsx")会遍历文件夹下所有 xlsx 文件。ignore_index=True的作用是让合并后的行索引重新从 0 开始,避免多张表索引重复。如果每张表表头一致,这个脚本可以直接使用。
需要注意:如果文件夹里包含格式不一致的文件,例如有的表多一列,有的表少一列,合并后可能会出现大量空值。建议先打印各文件的列名做一次检查。
5.5 按条件把数据拆分到多个文件
批量合并的逆操作是按某个字段拆分。比如一个总表包含了三个区域的订单,现在要按区域生成三个独立文件:
import pandas as pd df = pd.read_excel("原始数据/订单表.xlsx") output_dir = "输出结果/按区域拆分" import os os.makedirs(output_dir, exist_ok=True) for region, group in df.groupby("区域"): # 简单清理文件名,避免特殊字符导致保存失败 safe_name = str(region).replace("/", "-").replace("\\", "-") file_path = os.path.join(output_dir, f"订单_{safe_name}.xlsx") group.to_excel(file_path, index=False) print("已生成:", file_path)写文件之前先创建输出目录,避免因为目录不存在而报错。分组键里的值如果包含\、/、:等 Windows 不支持的符号,需要提前替换掉,否则保存时会抛异常。
5.6 根据 Excel 清单批量重命名 Word 文件
这个需求经常出现在文档管理场景:Excel 里维护着“原文件名”和“新文件名”两列,文件夹里有大量 Word 文档,需要按清单统一改名。
先安装 python-docx 之外的库?实际上这个需求本身不需要解析 Word 内容,只需要操作文件名,因此不需要 python-docx。但后续如果还需要修改 Word 内部内容,那才需要安装 python-docx。下面用 pandas 读取 Excel 清单后,用os.rename或Path.rename完成操作:
import pandas as pd from pathlib import Path df = pd.read_excel("原始数据/重命名清单.xlsx") folder = Path("原始数据/Word文档") for _, row in df.iterrows(): old_name = str(row["原文件名"]) + ".docx" new_name = str(row["新文件名"]) + ".docx" old_path = folder / old_name new_path = folder / new_name if old_path.exists() and not new_path.exists(): old_path.rename(new_path) print("已将", old_name, "重命名为", new_name) elif not old_path.exists(): print("未找到文件:", old_name) else: print("目标文件已存在,跳过:", new_name)这里故意加了一步判断,防止目标文件已存在时直接覆盖他人文件。批量重命名属于不可逆操作,如果没有提前备份,改错后恢复成本很高。最稳妥的做法是先把旧文件名和新文件名都打印出来,确认无误后再真正执行。
6. 批量化与自动化封装
到这里,我们已经跑通了多个独立场景。实际办公里,这些操作往往不是只跑一次,而是每天、每周重复执行。因此要把脚本封装成函数,并预留批量入口。
6.1 把核心流程封装成函数
以批量合并为例,封装后的函数应该接收“输入目录”和“输出文件路径”两个参数:
import pandas as pd from pathlib import Path def merge_excel_files(input_dir: str, output_path: str, pattern: str = "*.xlsx"): frames = [] for file in Path(input_dir).glob(pattern): if Path(output_path).name == file.name: continue df = pd.read_excel(file) frames.append(df) if not frames: print("没有需要合并的文件") return False merged_df = pd.concat(frames, ignore_index=True) merged_df.to_excel(output_path, index=False) print(f"合并完成,输出文件: {output_path}") return True if __name__ == "__main__": merge_excel_files("原始数据/销售数据", "输出结果/销售合并.xlsx")这样写的好处是:以后在其他项目里,只需要import merge_excel_files就能复用;脚本文件直接运行时也能完成一次合并任务。
6.2 用目录任务批处理多个输入输出
如果每天要向几个固定目录输出结果,可以再封装一个最简单的“任务字典”:
import os if __name__ == "__main__": tasks = [ {"input_dir": "原始数据/销售A", "output": "输出结果/销售A_合并.xlsx"}, {"input_dir": "原始数据/销售B", "output": "输出结果/销售B_合并.xlsx"}, ] for task in tasks: success = merge_excel_files(task["input_dir"], task["output"]) if not success: print("任务失败,请检查目录:", task["input_dir"])这个模式已经接近批量化。后续如果要支持更多任务类型,可以把任务配置抽到一个 JSON 文件里,脚本启动时读取配置,再循环执行。
6.3 加入日志与失败重试
批量处理最怕脚本跑到一半因为某个文件格式异常而中断。简单处理方式是记录成功和失败的文件名:
import pandas as pd from pathlib import Path import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") folder = Path("原始数据/销售数据") output_path = "输出结果/销售合并.xlsx" success_list = [] fail_list = [] frames = [] for file in folder.glob("*.xlsx"): if file.name == Path(output_path).name: continue try: df = pd.read_excel(file) frames.append(df) success_list.append(file.name) logging.info(f"成功读取: {file.name}") except Exception as e: fail_list.append(file.name) logging.error(f"读取失败: {file.name},原因: {e}") if frames: merged_df = pd.concat(frames, ignore_index=True) merged_df.to_excel(output_path, index=False) logging.info(f"成功: {len(success_list)} 个文件,失败: {len(fail_list)} 个文件") if fail_list: logging.warning(f"失败文件列表: {fail_list}")加了try-except后,单个文件出错不会导致整个脚本崩溃,日志里会记录具体失败原因和文件信息。这也是工程化脚本和一次性脚本的一个重要区别。
6.4 后续接 API 或定时任务的思路
这个基础主题本身不涉及模型服务或复杂的 Web API,但在实际办公自动化落地时,脚本能力很容易被二次封装。两个常见方向:
- 把函数暴露给 FastAPI 接口,让前端页面或业务系统上传 Excel,后端处理后返回结果文件。
- 用系统自带的任务计划程序每天定时执行脚本,例如每天早上 8 点自动读取指定目录文件,处理后输出到共享目录。
从基础脚本到 API 服务或定时任务,本质是一样的:先保证核心处理函数稳定,再在外部包装触发入口。建议不要把所有操作都写在一个巨大的脚本里,而是拆成“读取、清洗、合并、导出”几个模块,后续接任何触发方式都会更轻松。
7. 资源占用与性能观察
7.1 Excel 处理对硬件的需求
Excel 自动化不是模型推理任务,不需要 GPU,也不存在显存占用问题。主要关注的是:
- CPU:脚本在读取、遍历、拼接数据时消耗 CPU。
- 内存:openpyxl 和 pandas 都会把数据加载到内存中。文件越大,内存占用越明显。
- 磁盘:输出文件需要足够空间,批量处理时注意不要把大量文件写到系统盘。
对普通办公场景,几千到几万行数据、几十个文件以内的批量任务,一般办公电脑都能流畅处理。对于几十万行以上或上百MB 级别的超大文件,需要小心内存占用,可能需要改用逐行读取或数据库方案。
7.2 怎么观察脚本资源占用
可以在脚本运行时打开操作系统的任务管理器,找到对应的python进程。要更精确地记录,可以使用 psutil:
pip install psutil然后在处理过程中打印当前进程内存:
import psutil import os def print_memory_usage(): process = psutil.Process(os.getpid()) memory_mb = process.memory_info().rss / 1024 / 1024 print(f"当前内存占用: {memory_mb:.1f} MB")这个数字会随数据读取量变化。如果你发现批处理跑到一半内存快速上涨,说明代码可能把多个大文件同时保留在内存里了,需要调整处理策略。
7.3 大文件处理优化:使用 openpyxl 只读模式
openpyxl 在读取大型文件时,可以通过read_only=True进入只读模式,逐行读取而不是一次性加载整个工作表:
from openpyxl import load_workbook wb = load_workbook("原始数据/超大文件.xlsx", read_only=True) ws = wb.active for row in ws.iter_rows(values_only=True): # 逐行处理 print(row) wb.close()只读模式适合快速遍历数据,但会牺牲部分单元格对象能力。如果你只是做求和、统计、内容拼接,这种模式非常合适。
7.4 pandas 读取大文件时的注意事项
pandas 没有直接的“流式读取 Excel”模式,它是把整个文件的解析结果载入内存。面对超大文件,可以通过参数降低开销:
import pandas as pd df = pd.read_excel( "原始数据/超大文件.xlsx", usecols=["姓名", "部门", "工资"], # 只读取需要的列 dtype={"工号": str}, # 指定列类型,避免数字误读 )usecols可以明显减少内存占用,如果只需要某几列做匹配,建议只保留必要列。dtype可以避免工号、身份证号等长数字变成浮点或科学计数法。
7.5 批量任务如何降低出问题概率
批量处理时,建议做到“读一个文件、处理一个文件、释放一个文件”,不要让所有 DataFrame 都累计在一个变量里。比如需要把多个文件中的某几列汇总到一张新表,可以每读取一个文件就计算一次结果,最后只保存汇总值。
另一个策略是控制同时读取的文件数量。如果实在需要并行处理,也要考虑 CPU 核数和文件大小,直接开 100 个线程去读 Excel 并不一定能提速,反而容易把内存占满。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 导入 openpyxl 时报 ModuleNotFoundError | 依赖库没有安装 | 执行pip list查看包列表 | 运行pip install openpyxl |
| pandas 读取 xlsx 报 Missing optional dependency 'openpyxl' | 缺少读取 Excel 的引擎 | 按报错提示安装依赖 | 运行pip install openpyxl |
| 提示 PermissionError | Excel 文件正被 WPS 或 Excel 打开 | 关闭文件窗口,检查是否有进程占用 | 释放文件后重新执行,或把输出路径改为新文件 |
| openpyxl 无法打开 .xls 文件 | 格式是旧版 Excel | 查看文件后缀是否真的是 .xls | 用 Excel 另存为 .xlsx |
| 文件显示“外部表不是预期的格式” | 文件后缀与实际格式不一致,或文件损坏 | 用 Excel 打开后另存新 xlsx | 确认文件是标准 xlsx 格式 |
| 用 data_only=True 读取公式单元格返回 None | openpyxl 不自己计算公式结果 | 判断文件是否曾被 Excel 打开保存过 | 写入公式后用 Excel 打开一次,或让生成端使用计算后的缓存值 |
| 读出的数字变成科学计数法 | 单元格存为文本或 pandas 自动推断类型 | 打印 dtypes 检查类型 | 读取时指定 dtype 或转成字符串 |
| 中文路径导致读不到文件 | 系统编码或路径写法问题 | 打印绝对路径确认是否存在 | 使用 pathlib.Path 拼接路径,避免直接用\\手写路径 |
| 输出文件名覆盖原始文件 | 脚本保存路径设置错误 | 检查save()和to_excel()路径 | 输出单独放 output 目录,避免和输入目录相同 |
| 批量合并后数据条数翻倍 | 两张表存在一对多关系,或重复读取了输出文件 | 打印 文件列表,检查是否有重复来源 | 排除输出文件,匹配前先 remove 重复项 |
这里特别强调“外部表不是预期的格式”。这个报错不只出现在 Python 中,很多办公软件在连接 Excel 时也可能出现。最常见原因是文件扩展名是 .xlsx,但实际内容是 CSV 或旧格式文本,或者文件是从某个系统直接导出、头信息不完整。解决思路不是强行让 Python 读取,而是先打开文件确认格式,另存为规范 .xlsx 后重试。
9. 最佳实践与使用建议
9.1 原始数据永远备份
处理 Excel 前,先复制一份原始文件。批量重命名、批量覆盖、按条件拆分这类操作一旦跑错,结果文件很难手工恢复。最简单的方法是建立输入目录后只做读操作,所有结果写到输出结果/目录。这样即使脚本写得有问题,原始文件也不会被破坏。
9.2 第一次用少量数据验证
不要一上来就跑整个文件夹。先用一个只有几行数据的小文件测试,确认读取字段、输出格式、保存逻辑都正确后,再扩大到全量数据。全量运行前,可以打印文件数量、总行数、列名等统计信息,防止读错文件或表头不一致。
9.3 路径统一管理
脚本里的文件路径,尽量避免散落在代码各处。更稳妥的方式是在脚本开头定义:
INPUT_DIR = "原始数据" OUTPUT_DIR = "输出结果"然后用Path(INPUT_DIR) / "销售数据"拼接。这样目录变动时,只需要改顶部配置,不用到处查找。
9.4 定期任务要考虑目录是否存在
如果脚本要交给任务计划程序每天运行,输出目录很可能在某天被清理掉。创建目录时使用os.makedirs(output_dir, exist_ok=True),避免目录不存在时报错。运行日志也不要只输出到控制台,建议写入日志文件,方便排查“别人反馈脚本没跑”的问题。
9.5 加密和只读工作簿的坑
如果需要处理带打开密码的 Excel 文件,openpyxl 默认不支持解密读取。最简单的方式是先用 Excel 手动解密另存为无密码文件,或使用 office 自动化相关模块。只读或共享工作簿也可能出现权限限制,建议测试文件使用普通工作簿。
9.6 涉及关键业务数据时必须做校验
办公自动化脚本的常见风险是:代码“能跑”,但结果错。批量合并后,需要核对总行数是否等于各文件行数之和;匹配后,需要检查关键列空值数量是否合理;拆分后,需要查看各组行数之和是否等于原表总行数。这些校验逻辑可以以简单的assert或日志方式放在脚本末尾,第一时间发现数据异常。
9.7 敏感数据脱敏
测试脚本时,不要直接使用真实客户名单、工资全表。建议造一批“张三”“李四”或带随机编号的测试数据。如果一定要处理真实数据,输出文件不要带上手机号、身份证号等原始信息,只保留业务需要的字段。脚本本身也不要提交到公共代码仓库。
10. 总结与下一步
这一篇重点不是讲复杂算法,而是把 Python 处理 Excel 的完整链路走通:环境安装、依赖准备、读取、写入、填充、合并、匹配、拆分、批量重命名,再到批量化封装和问题排查。对刚接触办公自动化的读者,只要能把 5.4 的批量合并、5.3 的数据匹配两个案例跑通,就已经具备处理常见表格任务的脚本能力。
接下来比较容易踩的坑集中在三类:一是旧版 .xls 与 .xlsx 格式转换,二是文件被 Excel 占用导致无法写入,三是批量输出时路径和文件名不规范。在写任何全量脚本之前,先给这三个点打补丁,能省下大量调试时间。
如果再往下走,建议按这个顺序进阶:先掌握 pandas 的groupby和merge,这是数据匹配和汇总的核心;然后学习把清洗结果自动生成图表;最后把脚本接入目录监听或定时任务,让数据在每天早上自动完成更新。第一个可以练习的自动化项目,可以试试“每天合并当天新增的三个报表并生成一份汇总 Excel”,这几乎覆盖了办公自动化的全部基础套路。