查看: 111|回复: 3

Python批量处理多个Excel工作簿:工作表分类汇总与数值清洗实战

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
问题背景:手工汇总Excel报表的低效与风险

在日常办公中,经常需要处理一个文件夹里的多个Excel文件(工作簿),每个文件又包含多张工作表。领导要求按“销售区域”汇总“销售利润”。如果手工操作:打开工作簿→切换工作表→分类汇总→复制结果→保存,然后重复。工作簿数量一多,不仅慢,而且容易漏表、选错区域、金额列被当成文本、临时文件被误处理等。Python自动化可以把重复动作抽象成稳定流程,避免人为错误。

适用场景
这类脚本适合以下场景:一个文件夹内有多个.xlsx工作簿;每个工作簿有多张工作表;表头结构相同或相似;需要按某个字段分组(如销售区域、客户名称、部门、产品类型);需要对数值字段求和(如销售利润、销售额、数量、成本);希望汇总结果写回原工作表右侧,方便查看。建议先在测试文件夹复制2~3个样例文件试运行,确认无误后再处理正式数据,避免批量误操作损坏原始文件。

核心原理:批处理流水线
手工汇总步骤拆开后就是固定流水线:扫描文件夹→打开工作簿→遍历工作表→读取数据→分组汇总→写回保存。三个库各有分工:os负责文件系统处理(扫描、拼接路径、判断扩展名);xlwings负责操作Excel(打开工作簿、访问工作表、写回结果);pandas负责数据处理(将表格转为DataFrame,用groupby()分类汇总)。这条流程搭好后,只需替换中间的数据处理逻辑,即可实现批量筛选、排序、拆分等需求。

操作前准备
首先安装依赖:
  1. pip install pandas xlwings
复制代码
注意:xlwings在Windows上依赖本机已安装的Microsoft Excel。

建议目录结构:原始报表和输出结果分开存放。例如:项目目录下建“销售表”文件夹放原始文件,另建“输出结果”文件夹保存处理后的文件。

字段要求:每张工作表至少包含两个字段:销售区域、销售利润。如果字段名不同,需修改代码中的参数。

完整代码:批量处理多个工作簿中的所有工作表
以下代码考虑了真实办公中的常见问题:跳过~$临时文件、跳过空表、校验字段、清洗金额、另存输出、退出Excel。
  1. import os
  2. import pandas as pd
  3. import xlwings as xw
  4. def clean_to_number(series: pd.Series) -> pd.Series:
  5.     """
  6.     将可能带有货币符号、逗号、空格、文本前缀的金额列清洗为数值。
  7.     例如:¥12,345.67 -> 12345.67,profit: 6543.21 -> 6543.21
  8.     """
  9.     series = series.astype(str).str.strip()
  10.     series = series.str.replace(",", "", regex=False)
  11.     series = series.str.replace(r"[¥¥$ ]", "", regex=True)
  12.     series = series.str.replace(r"[^0-9\.\-]", "", regex=True)
  13.     return pd.to_numeric(series, errors="coerce")
  14. def summarize_one_sheet(
  15.     df: pd.DataFrame,
  16.     group_col: str = "销售区域",
  17.     value_col: str = "销售利润"
  18. ) -> pd.DataFrame:
  19.     """
  20.     对单张工作表数据进行分类汇总。按group_col分组,对value_col求和。
  21.     """
  22.     if group_col not in df.columns:
  23.         raise KeyError(f"缺少分组列:{group_col}")
  24.     if value_col not in df.columns:
  25.         raise KeyError(f"缺少汇总列:{value_col}")
  26.     temp = df.copy()
  27.     # 先清洗成数值,避免字符串求和或排序错误
  28.     temp[value_col] = clean_to_number(temp[value_col]).fillna(0)
  29.     result = (
  30.         temp.groupby(group_col, dropna=False)[value_col]
  31.         .sum()
  32.         .reset_index()
  33.         .rename(columns={
  34.             group_col: "销售区域",
  35.             value_col: "销售利润汇总"
  36.         })
  37.         .sort_values("销售利润汇总", ascending=False)
  38.     )
  39.     return result
  40. def batch_summary_workbooks(
  41.     input_folder: str,
  42.     output_folder: str,
  43.     group_col: str = "销售区域",
  44.     value_col: str = "销售利润",
  45.     start_cell: str = "A1",
  46.     write_cell: str = "J1"
  47. ) -> None:
  48.     """
  49.     批量处理多个工作簿中的所有工作表。
  50.     """
  51.     os.makedirs(output_folder, exist_ok=True)
  52.     app = xw.App(visible=False, add_book=False)
  53.     app.display_alerts = False
  54.     app.screen_updating = False
  55.     try:
  56.         for file_name in os.listdir(input_folder):
  57.             # 跳过Excel临时文件和非xlsx文件
  58.             if file_name.startswith("~$"):
  59.                 continue
  60.             if not file_name.lower().endswith(".xlsx"):
  61.                 continue
  62.             input_path = os.path.join(input_folder, file_name)
  63.             output_path = os.path.join(output_folder, file_name)
  64.             print(f"\n[OPEN] 正在处理工作簿:{input_path}")
  65.             wb = app.books.open(input_path)
  66.             success_count = 0
  67.             skip_count = 0
  68.             try:
  69.                 for sht in wb.sheets:
  70.                     try:
  71.                         rng = sht.range(start_cell).expand("table")
  72.                         if rng.value is None:
  73.                             print(f" [SKIP] {sht.name}:空表")
  74.                             skip_count += 1
  75.                             continue
  76.                         df = rng.options(pd.DataFrame, header=1, index=False).value
  77.                         if df is None or df.empty:
  78.                             print(f" [SKIP] {sht.name}:无有效数据")
  79.                             skip_count += 1
  80.                             continue
  81.                         summary_df = summarize_one_sheet(
  82.                             df,
  83.                             group_col=group_col,
  84.                             value_col=value_col
  85.                         )
  86.                         # 清理旧汇总区,避免上一次结果残留
  87.                         sht.range(write_cell).resize(100, 3).clear_contents()
  88.                         # 写回汇总结果,不写入DataFrame索引
  89.                         sht.range(write_cell).options(index=False).value = summary_df
  90.                         sht.autofit()
  91.                         print(f" [OK] {sht.name}:已汇总到 {write_cell}")
  92.                         success_count += 1
  93.                     except Exception as e:
  94.                         print(f" [SKIP] {sht.name}:{e}")
  95.                         skip_count += 1
  96.                 wb.save(output_path)
  97.                 print(f"[DONE] 已保存:{output_path},成功 {success_count} 张表,跳过 {skip_count} 张表")
  98.             finally:
  99.                 wb.close()
  100.     finally:
  101.         app.quit()
  102.     print("\n[ALL DONE] 所有工作簿处理完成")
  103. if __name__ == "__main__":
  104.     batch_summary_workbooks(
  105.         input_folder=r"销售表",
  106.         output_folder=r"输出结果",
  107.         group_col="销售区域",
  108.         value_col="销售利润",
  109.         start_cell="A1",
  110.         write_cell="J1"
  111.     )
复制代码

关键判断:必须先清洗数值再分类汇总
很多新手直接写 df.groupby("销售区域")["销售利润"].sum(),但真实Excel报表中销售利润列可能带有货币符号(¥、$)、逗号、空格、文本前缀(如"profit: ")等。如果不清洗,pandas会识别为字符串,求和结果异常甚至报错。代码中的clean_to_number函数先用正则去除符号和文本,再转为数值,确保分组求和结果准确。

运行效果验证
脚本运行后不能只看控制台无报错,需要验证三层:
1. 输出文件夹中生成了对应的Excel文件,数量与输入文件一致。
2. 打开任意输出文件,切换到不同工作表,查看J1位置是否出现汇总结果(两列:“销售区域”和“销售利润汇总”)。
3. 随机选一张表,用Excel透视表或筛选核对一两个区域的销售利润,与脚本结果一致才算可信。

常见问题与踩坑记录
- 为什么脚本会跳过某些工作表?常见原因:空表、表头不在A1、缺少指定字段。代码已做异常捕获,不会中断批处理。如果表头从A2开始,请修改start_cell参数。
- 为什么要跳过~$开头的文件?这些是Excel打开的临时锁定文件,不是数据文件,参与处理会导致打开失败或权限错误。
- 为什么输出到新文件夹而不是覆盖原文件?分类汇总属于批量写操作,写错可能影响多个文件。输出到新文件夹后可以对比检查,确认无误再替换原始文件。
- 为什么Excel有时会残留进程?脚本中途异常退出而未执行app.quit()会导致Excel进程残留。代码使用try/finally确保无论是否报错都调用app.quit(),这是使用xlwings的基本规范。

总结:从脚本到办公自动化套路
本文的核心不是记住某行代码,而是理解可复用的套路:先定位文件,再定位工作簿,再定位工作表,最后将数据读入DataFrame处理。这套思路可以扩展到按客户名称汇总销售额、按部门统计费用、按产品类型汇总订单数量,甚至将每个工作簿的汇总结果合并成总表。批量处理脚本一定要先用样例数据验证,再处理正式数据,尤其是涉及金额汇总、财务报表时。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python批量处理多个Excel工作簿:工作表分类汇总与数值清洗实战

这个脚本写得很实用,特别是对“~$”临时文件和字段校验的处理很周全,能在正式跑数据前避免不少坑。数值清洗那部分对带货币符号和逗号的金额也很友好,实际工作中很多报表确实不干净。 有一点想请教:输出结果写回原工作簿的右侧(比如J列),如果原工作表的数据列数超过J列,或者后面有其他公式,会不会有覆盖的风险?是否考虑过增加一个“新工作表写入结果”的选项?另外,`start_cell`参数好像没有在函数里用到,是不是留着给其他扩展场景预留的? 整体流程清晰,注释也到位,对新手很友好。建议加一个对文件编码或单元格格式的说明,比如金额列清洗后保持数字格式,免得导出后还是文本。感谢分享。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python批量处理多个Excel工作簿:工作表分类汇总与数值清洗实战

您这套脚本思路很清晰,把日常手工汇总的痛点(临时文件、文本金额、空表等)都考虑进去了,特别是 `clean_to_number` 函数对货币符号和逗号的处理很实用。我平时也常用类似方式批量处理报表,不过有个小建议:如果源文件的`销售区域`列有合并单元格或空值,groupby(dropna=False)已经处理了空值,但合并单元格读进来可能会有前一行重复填充的问题,可以在读表时先 `ffill()` 一下。另外,xlwings 在 Excel 未安装的环境下会报错,您一般在正式跑之前会怎么快速校验环境?
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python批量处理多个Excel工作簿:工作表分类汇总与数值清洗实战

感谢分享!这个脚本设计得很实用,尤其是对办公中常见的脏数据(货币符号、逗号、空格、文本前缀)做了清洗,还考虑了临时文件`~$`和空表的情况,很贴心。我平时也常被Excel汇总折磨,手动操作容易漏数据,有了这个流水线就稳多了。有个小问题想请教:如果不同工作簿的表头字段名不完全一致,比如有的叫“销售区域”,有的叫“区域”,需要手动修改`group_col`参数吗?还是说脚本里可以加个字段映射逻辑?另外,输出结果写回原工作表右侧(比如J列),如果原表右侧已有其他数据,会不会被覆盖?希望楼主能再多讲一下这部分细节。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-30 13:46 , Processed in 0.040845 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部