手动合并 200 份 Excel 要 3 天,Python 半小时搞定
- 2026-11-09 05:09:05
200 个部门各交了一份 Excel 报表,格式统一、内容不同,领导让你"汇总成一张总表,周一交"。打开一个,复制,粘贴;再打开一个,复制,粘贴……200 个文件,手速快也得一整天,中间一不留神还会粘错行、漏数据,返工更崩溃。
用 Python 呢?核心代码十几行,跑一遍,半小时连"合并 + 分类汇总 + 生成报告"全部搞定。改改路径和列名就能用。
| 01 | 先看我们要处理的东西 |
每个部门一个 Excel 文件,列名统一(这是前提):
目录结构:
reports/├── 部门A.xlsx├── 部门B.xlsx├── 部门C.xlsx└── ...(共200个)目标:把 200 个文件合成一张总表,再按"商品"汇总出总数和总金额。
| 02 | 第一步:合并 200 个 Excel(pandas 核心代码) |
import pandas as pdimport glob# 找到 reports 目录下所有 xlsx 文件files = glob.glob("reports/*.xlsx")# 全部读进来,拼成一个大 DataFrameall_dfs = [pd.read_excel(f) for f in files]merged = pd.concat(all_dfs, ignore_index=True)print(f"共合并 {len(files)} 个文件,{len(merged)} 行数据")merged.to_excel("合并总表.xlsx", index=False)三行核心代码,200 个文件就并完了。pd.concat 按行拼接,ignore_index=True 让行号重新编号,不重复。
| 03 | 第二步:按商品分类汇总(老板真正要的数字) |
合并只是开始,老板要的是汇总结果:
# 按商品分组,求总数量、总金额summary = merged.groupby("商品").agg( 总数量=("数量", "sum"), 总金额=("金额", "sum"),).reset_index()# 按金额从高到低排个序,方便看summary = summary.sort_values("总金额", ascending=False)# 加一行合计summary.loc[len(summary)] = ["合计", summary["总数量"].sum(), summary["总金额"].sum()]summary.to_excel("汇总结果.xlsx", index=False)print(summary)输出长这样:
"总数""总额""哪个卖得多",一眼就能看出来。
| 04 | 第三步:给表格加上格式(openpyxl 补样式) |
pandas 导出的 Excel 是纯数据,没有格式。想加粗表头、调列宽、标红金额低的项,用 openpyxl 再加工一下:
from openpyxl import load_workbookfrom openpyxl.styles import Font, PatternFill, Alignmentwb = load_workbook("汇总结果.xlsx")ws = wb.active# 表头加粗 + 灰色底header_fill = PatternFill("solid", fgColor="D9E1F2")for cell in ws[1]: cell.font = Font(bold=True) cell.fill = header_fill cell.alignment = Alignment(horizontal="center")# 列宽自适应(粗略按内容长度)for col in ws.columns: max_len = max(len(str(c.value)) if c.value else 0 for c in col) ws.column_dimensions[col[0].column_letter].width = max_len + 4# 金额列:小于10万的标红,提醒"这个品类卖得少"for row in ws.iter_rows(min_row=2, min_col=3, max_col=3): for cell in row: if isinstance(cell.value, (int, float)) and cell.value < 100000: cell.font = Font(color="FF0000", bold=True)wb.save("汇总结果_带格式.xlsx")print("带格式的汇总表已生成")这样交上去的是一份能直接发群的表,不是一堆裸数据。
| 05 | 完整的可直接抄版本 |
把上面三步拼成一个完整脚本,改两个地方就能用:reports 目录路径、商品/数量/金额 这几个列名。
"""多 Excel 合并 + 分类汇总 + 生成报告用法:把要合并的 xlsx 放在 reports/ 下,运行本脚本"""import pandas as pdimport globfrom openpyxl import load_workbookfrom openpyxl.styles import Font, PatternFill, Alignmentdef merge_excels(folder="reports", output="合并总表.xlsx"): """合并文件夹下所有 xlsx""" files = glob.glob(f"{folder}/*.xlsx") if not files: print("没有找到 Excel 文件") return all_dfs = [pd.read_excel(f) for f in files] merged = pd.concat(all_dfs, ignore_index=True) merged.to_excel(output, index=False) print(f"合并完成:{len(files)} 个文件,{len(merged)} 行") return mergeddef summarize(merged, output="汇总结果_带格式.xlsx", group_col="商品", sum_cols=("数量", "金额")): """按列分组汇总 + 加格式""" summary = merged.groupby(group_col)[list(sum_cols)].sum().reset_index() summary = summary.sort_values(sum_cols[1], ascending=False) summary.loc[len(summary)] = [f"合计"] + [summary[c].sum() for c in sum_cols] summary.to_excel(output, index=False) wb = load_workbook(output) ws = wb.active header_fill = PatternFill("solid", fgColor="D9E1F2") for cell in ws[1]: cell.font = Font(bold=True) cell.fill = header_fill cell.alignment = Alignment(horizontal="center") for col in ws.columns: max_len = max(len(str(c.value)) if c.value else 0 for c in col) ws.column_dimensions[col[0].column_letter].width = max_len + 4 wb.save(output) print(f"汇总报告已生成:{output}")if __name__ == "__main__": data = merge_excels() if data is not None: summarize(data)| 06 | 实际跑起来会碰到的问题 |
列名必须统一。 只要有一个文件的列名不一样(比如"商品名"和"商品"),合并后就会多出一列或者错位。稳妥做法:合并前先检查所有文件的列名是否一致——
col_sets = [set(pd.read_excel(f).columns) for f in files[:5]] # 先抽查5个if len(set.union(*col_sets)) != len(col_sets[0]): print("警告:列名不一致,检查下面这些文件:") for f in files: cols = set(pd.read_excel(f).columns) if cols != col_sets[0]: print(f" {f}: {cols}")金额列可能是文本格式。 有些报表的金额列是文本(Excel 里左上角有绿色小三角的那种),groupby 求和时直接报错或求和为 0。加一句强转:
merged["金额"] = pd.to_numeric(merged["金额"], errors="coerce").fillna(0)errors="coerce" 会把无法转换的文本变成 NaN,再 fillna(0) 兜底。这样即使有几行数据格式不对,也不会让整个汇总挂掉。
文件里有合并单元格的表头。 有些报表的第一行是大标题("XX部门月度报表"),第二行才是真表头。pd.read_excel(f) 默认把第一行当表头,数据会错位。处理办法:读的时候指定 header=1(第二行才是真表头),或者用 skiprows 跳过空行。如果不同部门表头位置还不一样,那就得逐个文件判断了——这通常是和同事协商统一格式的动力来源。
文件超过几千个或者单个文件特别大。pd.read_excel 读一个文件就要占一块内存。200 个普通报表没问题,但如果文件量级更大,可以分批合并:每 50 个文件 concat 一次、存一个中间文件,最后再把中间文件合起来,避免内存撑爆。另外 .xls(老版 Excel)和 .xlsx 用的读取引擎不同,如果混用要装 xlrd:
files = glob.glob("reports/*.xlsx") + glob.glob("reports/*.xls")# .xls 需要 xlrd:pip install xlrd| 07 | 用熟之后能扩展的方向 |
groupby 能做远不止 sum。比如你想同时看每个商品的平均单价、最大单笔金额:
summary = merged.groupby("商品").agg( 总数量=("数量", "sum"), 总金额=("金额", "sum"), 平均单价=("单价", "mean"), 最大单笔=("金额", "max"),).reset_index()如果还想给每个部门单独出一份汇总(而不是全局一份),加一层 groupby(["部门", "商品"]) 就行——前提是你的原始数据里有"部门"列。可以在合并时给每个 DataFrame 加一列部门标识:
all_dfs = []for f in files: df = pd.read_excel(f) df["来源文件"] = Path(f).stem # 从文件名提取部门名 all_dfs.append(df)【嗨翻Python】