使用Python对Excel工作簿中的所有工作表分别求和并自动生成结果
作者:杨利杰YJlio
1. 问题背景:几十个 Sheet 都要合计,手工做很痛苦
这一篇继续整理《超简单:用 Python 让 Excel 飞起来》第 6 章中的案例内容,主题是 对一个工作簿中的所有工作表分别求和。这个场景非常贴近真实办公:一个 Excel 工作簿里按月份、部门、项目或区域拆了很多 Sheet,每张表结构类似,都有一列金额,现在需要分别计算每张表的合计值。
手工当然也能做。打开第一张表,找到金额列,写一个 SUM;再打开第二张表,重复一次;几十张表下来,本质就是重复点鼠标、重复复制公式、重复检查结果。表少的时候还能忍,表一多就容易漏算、错算、重复算。
这张图展示了本文的整体主题:使用 Python 对一个工作簿中的所有工作表分别求和,并自动生成结果。

从这张图中可以看出,本文的重点不是单独计算某一个 Sheet,而是把“遍历所有 Sheet → 指定列求和 → 写回合计结果 → 生成汇总表”做成一条完整自动化流程。真正有价值的不是 sum() 这一个函数,而是把求和任务变成可重复执行的批处理模板。
这里最容易犯的错误,是把这个案例理解成简单求和。实际工作中更麻烦的是:有的 Sheet 是空表,有的 Sheet 缺少目标列,有的金额看起来是数字,其实是文本,还有的工作簿已经存在“汇总”Sheet。脚本要能处理这些边界,才算真正可用。
2. 目标效果:运行一次脚本想看到什么
这个案例的目标效果可以拆成两层。第一层是在每张业务工作表底部自动追加一行 合计,并把指定金额列的合计值写进去。第二层是额外生成一个 汇总 工作表,把每张 Sheet 的合计结果集中展示出来。
这张图展示了第一层效果:脚本会在每张工作表底部自动写入合计行。

从这张图中可以看出,合计结果不是覆盖原始数据,而是追加在表格末尾。这个设计比较稳,因为原数据结构不会被破坏,读者打开每张表时,也能直接看到该表自己的合计结果。
推荐把合计结果写在表尾,而不是直接插入到数据中间。这样既方便阅读,也减少破坏原有数据结构的风险。如果后续要再次处理原始明细,表尾合计行也更容易识别和排除。
最终我们希望得到这样的交付结果:
1. 每张业务 Sheet:
- 表尾追加“合计”行
- 指定金额列写入合计值
2. 汇总 Sheet:
- 列出每张工作表名称
- 列出每张表的金额合计
- 最后一行给出总计
这套输出适合交付领导或业务同事:既能看每张表的局部结果,也能在汇总表里横向对比所有 Sheet。
3. 实现思路:把求和变成标准化流水线
要想让脚本稳定,就不能只盯着“求和”这一步。完整流程应该是:打开工作簿,遍历所有工作表,读取表格数据,清洗金额列,计算合计,写回当前 Sheet,记录到汇总列表,最后生成汇总 Sheet 并保存。
这张图展示了自动求和的完整流程,从打开工作簿到生成汇总表,每一步都对应脚本中的关键动作。

从这张图中可以看出,一个可靠的自动化脚本必须具备“遍历、清洗、计算、写回、汇总”五个动作。如果缺少清洗,金额可能算错;如果缺少写回,单表结果不直观;如果缺少汇总,领导仍然看不到整体对比。
我把这一节理解成一个可复用模块。输入是工作簿路径和要合计的列名,输出是每个 Sheet 的合计行和一个总汇总表。后续如果要计算最大值、最小值、平均值,其实也能沿用同一套框架,只是把计算函数换掉。
4. 环境准备:pandas 负责计算,xlwings 负责写回 Excel
这个案例里我使用的组合是 pandas + xlwings。其中 pandas 负责数据读取后的计算和清洗,xlwings 负责打开工作簿、遍历工作表、把结果写回 Excel。
pandas 擅长算数据,xlwings 擅长操作 Excel 应用。这两个工具组合在一起,适合处理这种“既要计算,又要写回原工作簿结构”的任务。
安装命令如下:
pip install pandas xlwings
如果你的电脑没有安装 Excel,或者 Excel 被安全策略、加载项、弹窗卡住,xlwings 可能无法正常工作。这不是 Python 语法问题,而是 Excel 应用环境问题。在企业桌面环境中,这一点要提前确认。
推荐先复制一份测试工作簿,不要直接对正式文件运行脚本。尤其是脚本涉及写回、保存、另存为这些动作时,先用样本文件跑通,再处理正式数据。
5. 完整代码:对所有 Sheet 分别求和并生成汇总表
下面这份代码是一个相对完整的版本,重点考虑了几个真实场景:自动跳过空表,自动跳过缺少目标列的 Sheet,自动跳过 汇总 Sheet,金额列支持 ¥12,345.67 这类文本数字,写回时只在表尾追加合计行,不覆盖原始明细数据。
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:
"""
对一个工作簿中的所有工作表分别对 sum_col 求和:
1. 在每个 Sheet 末尾追加“合计”行
2. 生成或覆盖一个“汇总”Sheet,列出每个 Sheet 的合计和总计
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:
# 跳过汇总 Sheet,避免二次运行时把汇总表也拿去求和
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
# 找到 sum_col 在表格中的列序号,xlwings 使用 1-based 列号
col_idx = df.columns.get_loc(sum_col) + 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}")
# 生成汇总 Sheet
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)
# 如果已存在汇总 Sheet,则清空;不存在则新建
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 = last.row + 1
sum_sht.range((total_row, 1)).value = "总计"
sum_sht.range((total_row, 2)).value = grand_total
try:
sum_sht.range((1, 1), (total_row, 2)).api.Columns.AutoFit()
sum_sht.range((total_row, 1), (total_row, 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",
)
这份代码的结构并不复杂,但边界处理比较重要。它不是简单读取所有 Sheet 后直接求和,而是先排除汇总表,再判断是否为空表,再判断目标列是否存在,最后才进入金额清洗和求和逻辑。
如果你直接把所有 Sheet 都拿来求和,下一次运行脚本时,汇总表也可能被重复计算,结果会越来越怪。所以跳过 summary_sheet_name 不是锦上添花,而是必须保留的安全判断。
6. 关键点拆解:金额列看着像数字,其实可能是字符串
这个案例最容易翻车的地方,不是 sum() 不会用,而是金额列的数据类型不稳定。Excel 里很多金额看起来像数字,但实际可能是文本,例如 ¥12,345.67、12,345、 8000 ,甚至混有空格和货币符号。
这张图展示了金额清洗的核心过程:把原始金额文本去掉符号、逗号和无效字符,再转换成真正可以求和的数值。

从这张图中可以看出,求和前的关键动作是“清洗转换”。如果金额列没有先转成数值,脚本可能得到 0、NaN,或者产生错误结果。这类问题表面上像是计算错误,实际上是数据类型问题。
清洗函数的核心代码如下:
def clean_to_number(s: pd.Series) -> pd.Series:
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")
这段代码做了四件事:第一,把数据统一转成字符串;第二,去掉逗号;第三,去掉人民币符号、美元符号和空格;第四,只保留数字、小数点和负号,再用 pd.to_numeric() 转成数值。
涉及金额统计时,我建议永远先做类型清洗,再做求和。不要相信 Excel 里“看起来像数字”的显示效果。脚本处理的是底层数据类型,不是你眼睛看到的表格样式。
7. 效果验证:如何确认脚本真的成功
脚本运行完成,不代表结果一定正确。对这种批量求和脚本,至少要验证三件事:每张业务 Sheet 是否写入了合计行;汇总 Sheet 是否生成;汇总 Sheet 的总计是否等于各 Sheet 合计之和。
这张图展示了最终汇总效果:多个工作表的合计结果集中到一张汇总表中,并可以横向对比。

从这张图中可以看出,汇总表不只是把结果堆在一起,它还承担了横向对比的作用。哪个部门、哪个月份、哪个项目金额最高,一眼就能看出来。这才是自动生成汇总表的真正价值:减少人工统计,也减少人工解释。
建议验证时按下面几个点检查:
1. 打开每个业务 Sheet,确认表尾是否出现“合计”行
2. 检查目标金额列是否写入正确合计值
3. 打开“汇总”Sheet,确认每张表都有一条汇总记录
4. 检查“总计”是否等于所有 Sheet 合计之和
5. 抽查 1 到 2 张 Sheet,用 Excel 自带 SUM 手工核对一次
如果想在脚本中增加简单验证,也可以打印每个 Sheet 的处理日志:
print(f"[OK] {sht.name}: {sum_col} 合计 = {total}")
推荐保留控制台日志。批量脚本不应该静默运行。哪些 Sheet 成功、哪些 Sheet 跳过、跳过原因是什么,都应该能从日志里看出来。这是后续复盘和排错的基础。
8. 常见问题与踩坑记录
8.1 expand(“table”) 依赖连续表格
sht.range(start_cell).expand("table") 的好处是方便,它会从起始单元格自动扩展读取连续区域。但它也有前提:表格中间不能有完全空白行或空白列。
如果你的表格中间有空行,expand 可能读不全。这种情况下,建议先统一模板,或者改成固定读取范围,例如 A1:H2000。
8.2 汇总 Sheet 必须跳过
如果脚本第一次运行后生成了 汇总 Sheet,第二次再运行时,如果不跳过它,脚本可能会把汇总表也当成业务表继续求和。
if sht.name == summary_sheet_name:
continue
这行代码非常关键。只要脚本会生成结果 Sheet,就应该在遍历时明确跳过结果 Sheet。
8.3 不要默认每张表都有目标列
真实工作簿里经常会出现模板不统一的问题。有的 Sheet 叫 销售利润,有的可能叫 利润 或 销售金额。如果不判断列名,脚本会直接报错。
if sum_col not in df.columns:
print(f"[SKIP] {sht.name}: 缺少列 '{sum_col}'")
continue
这个判断的意义,是让异常 Sheet 被识别并跳过,而不是让一个异常 Sheet 中断整个工作簿的处理。
8.4 不建议直接覆盖原文件
虽然代码支持 save_as=None 覆盖保存原文件,但我不建议初次使用时这么做。尤其是脚本会写回每张表、生成汇总 Sheet,一旦逻辑有误,原文件就可能被污染。
推荐使用另存为模式:
save_as="产品销售统计表_已求和.xlsx"
这样原始文件不会被破坏,处理结果也更方便对比。
9. 总结提升:这节真正要学的是批量计算框架
这一节看起来只是对每张工作表求和,但真正值得带走的是一套批量计算框架:遍历 Sheet、识别目标列、清洗数据、执行计算、写回结果、生成汇总表、最后验证输出。
如果只会写 sum(),遇到真实办公数据很快就会卡住;如果理解了这套流程,后续要做最大值、最小值、平均值、按条件求和、多列汇总,其实都是在同一个框架上替换计算逻辑。
办公自动化脚本的价值,不是让代码看起来复杂,而是让重复动作变得稳定、可验证、可复盘。这一点比单纯记住某个函数更重要。
最后再提醒一次:批量写回 Excel 前一定要先备份。自动化脚本跑得越快,错误扩散得也越快。先用样本文件测试,确认合计行、汇总表、总计结果都正确,再处理正式工作簿。
以上就是使用Python对Excel工作簿中的所有工作表分别求和并自动生成结果的详细内容,更多关于Python Excel工作表求和的资料请关注脚本之家其它相关文章!
