在办公自动化中,经常遇到一个 Excel 工作簿包含几十个结构相似的工作表(如按月份、部门拆分),需要分别对每个工作表的金额列求和,并集中展示结果。手动逐表添加 SUM 公式不仅重复低效,还容易遗漏或出错。本文将基于 pandas 和 xlwings 实现一套完整的批量求和脚本,自动遍历所有工作表、清洗金额数据、将合计结果写回原表末尾,并生成一张汇总表用于横向对比。
- pip install pandas xlwings
复制代码
安装前确认环境中有 Excel 应用(xlwings 依赖 Excel 进程),建议先复制一份测试文件再运行正式数据。
完整脚本如下,已处理空表、缺少目标列、金额带货币符号等常见边界情况:
- import pandas as pd
- import xlwings as xw
- def clean_to_number(s: pd.Series) -> pd.Series:
- """
- 将带货币符号、逗号、空格的文本金额转换为数值。
- 例如:¥12,345.67 -> 12345.67
- """
- s = s.astype(str).str.strip()
- s = s.str.replace(",", "", regex=False)
- s = s.str.replace(r"[¥¥$ ]", "", regex=True)
- s = s.str.replace(r"[^0-9\.\-]", "", regex=True)
- return pd.to_numeric(s, errors="coerce")
- def sum_all_sheets_in_workbook(
- input_xlsx: str,
- sum_col: str = "销售利润",
- summary_sheet_name: str = "汇总",
- start_cell: str = "A1",
- save_as: str | None = None,
- ) -> None:
- """
- 对工作簿中所有工作表按指定列求和:
- 1. 在每个工作表末尾追加“合计”行
- 2. 生成或覆盖“汇总”工作表,列出每个工作表的合计和总计
- save_as=None 则覆盖保存原文件,save_as=路径则另存为新文件
- """
- app = xw.App(visible=False, add_book=False)
- app.display_alerts = False
- app.screen_updating = False
- try:
- wb = app.books.open(input_xlsx)
- results = []
- for sht in wb.sheets:
- if sht.name == summary_sheet_name:
- continue # 跳过汇总表,避免重复计算
- rng = sht.range(start_cell).expand("table")
- if rng.value is None:
- print(f"[SKIP] {sht.name}: 空表")
- continue
- df = rng.options(pd.DataFrame).value
- if df is None or df.empty:
- print(f"[SKIP] {sht.name}: 无有效数据")
- continue
- if sum_col not in df.columns:
- print(f"[SKIP] {sht.name}: 缺少列 '{sum_col}'")
- continue
- # 清洗金额列并求和
- num = clean_to_number(df[sum_col]).fillna(0)
- total = float(num.sum())
- # 写回当前表末尾
- last_cell = rng.last_cell
- total_row = last_cell.row + 1
- col_idx = df.columns.get_loc(sum_col) + 1 # xlwings 列号从1开始
- sht.range((total_row, 1)).value = "合计"
- sht.range((total_row, col_idx)).value = total
- try:
- sht.range((total_row, 1), (total_row, col_idx)).api.Font.Bold = True
- except Exception:
- pass
- results.append({"工作表": sht.name, f"{sum_col}合计": total})
- print(f"[OK] {sht.name}: {sum_col} 合计 = {total}")
- # 生成汇总工作表
- summary_df = pd.DataFrame(results)
- if summary_df.empty:
- raise RuntimeError("未得到任何可汇总结果,请检查列名、数据区域或是否为空表。")
- total_col = f"{sum_col}合计"
- grand_total = float(summary_df[total_col].sum())
- summary_df = summary_df.sort_values(by=total_col, ascending=False)
- try:
- sum_sht = wb.sheets[summary_sheet_name]
- sum_sht.clear()
- except Exception:
- sum_sht = wb.sheets.add(summary_sheet_name, before=wb.sheets[0])
- sum_sht.range("A1").options(index=False).value = summary_df
- last = sum_sht.range("A1").expand("table").last_cell
- total_row_s = last.row + 1
- sum_sht.range((total_row_s, 1)).value = "总计"
- sum_sht.range((total_row_s, 2)).value = grand_total
- try:
- sum_sht.range((1, 1), (total_row_s, 2)).api.Columns.AutoFit()
- sum_sht.range((total_row_s, 1), (total_row_s, 2)).api.Font.Bold = True
- except Exception:
- pass
- if save_as:
- wb.save(save_as)
- print(f"[DONE] 已另存为:{save_as}")
- else:
- wb.save()
- print(f"[DONE] 已覆盖保存:{input_xlsx}")
- wb.close()
- finally:
- app.quit()
- if __name__ == "__main__":
- sum_all_sheets_in_workbook(
- input_xlsx="产品销售统计表.xlsx",
- sum_col="销售利润",
- summary_sheet_name="汇总",
- start_cell="A1",
- save_as="产品销售统计表_已求和.xlsx",
- )
复制代码
关键点解析:
1. 金额清洗:Excel 中金额常混有货币符号、逗号、空格等,必须先用正则去除,再通过 pd.to_numeric 转数值。clean_to_number 函数是求和正确的前提。
2. 跳过汇总表:脚本运行时生成的“汇总”表不能再次被遍历求和,否则结果会线性膨胀。if sht.name == summary_sheet_name: continue 是安全判断。
3. 容错处理:空表、缺少目标列的 Sheet 会被跳过并打印日志,不会中断整个工作簿。
4. 写回方式:在原表最后一行下方追加“合计”行,不覆盖原始数据。
5. 另存为建议:首次使用时指定 save_as 为另一路径,避免原始文件被误改。
效果验证:
- 打开每个业务 Sheet,确认表尾出现“合计”行且金额列有值。
- 打开“汇总”Sheet,检查是否包含所有工作表及总计。
- 随机选择1-2个 Sheet 用 Excel 的 SUM 公式手工核对。
- 控制台输出的日志可作为运行记录。
常见踩坑:
- expand("table") 依赖连续区域,表格中间有空行会导致读取不全,建议统一模板或改用固定范围(如 A1:H2000)。
- 不要默认每张表都有目标列,缺少时跳过而非报错。
- 不要直接覆盖原文件,除非已充分测试。
总结:
本文提供的不仅是求和函数,而是一个批量计算框架:遍历、清洗、计算、写回、汇总。替换 sum() 为 max/min/mean 就能扩展功能。办公自动化的价值在于让重复操作稳定、可验证。务必先备份再执行脚本。 |