多个 Excel 工作簿按姓名合并工资表:同名行自动相加

2026-10-05 · 喵窝资源铺

高频场景:技术部、市场部、外包部各发一份工资表,领导要一张合计表——同一个人出现在多份表里(还有外包),要跨簿把 3 月/4 月/5 月的数按人相加。用 VLOOKUP 硬凑,人一多、月份一多就又慢又容易漏一行。这篇给一个 60 行的 openpyxl 脚本:python3 merge_salary.py 'src/*.xlsx' 合并.xlsx 一条命令出结果。文中代码实跑验证,真实输出贴在下面。

它做的四件事

需求脚本的处理
多簿多表通配符喂进来,每个工作簿的每个工作表都扫一遍
同名行相加「王喵」在 3 份表里各一行,数值列自动累计成一行
文本型数字'3,000'、'¥3,000' 这类文本自动清洗成数字再计入
表头校验哪份表表头对不上,直接报错指出文件名,不静默拼出错的表

三个实跑才抓到的坑

坑1:Excel 里的 "3000" 经常是文本,不是数字。造测试数据时故意在一份表里把工资存成字符串 '3000',第一版脚本用 isinstance(v,(int,float)) 判断,结果这 3000 被直接漏加——合计表少钱,这可是工资!真实世界这种数据太常见了(系统导出、手工粘贴都会带出来)。正确做法是先把字符串清洗:replace(',','').replace('¥','') 再尝试转 float,转不动的(真正的备注文字)才跳过。修完之后 '3000' 被正确计入,合计 6500。
坑2:表头不一致必须报错,不能各拼各的。故意造了一份表头是「名字」而不是「姓名」的表,如果脚本静默跳过,那一整个部门的人就整行消失了。正确做法是发现表头对不上就 raise SystemExit 并指出是哪个文件——宁可明确失败,不要静默出错(跟之前 PDF 拆分那篇的教训一脉相承)。
坑3:人名别用顺序对齐,必须用姓名做 key。第一反应是「第2行加第2行」,但两个部门的人员名单不保证同序同人数——测试里市场部 3 人、技术部 2 人,按行号对齐直接把赵汪的工资加到李汪头上。必须以第一列(姓名)为字典 key 求和,输出时排序保证结果可复现。

脚本全文(merge_salary.py)

# -*- coding: utf-8 -*-
"""多工作簿按姓名合并工资/流水:同名工作表、同名行数值列相加。
用法: python3 merge_salary.py 'src/*.xlsx' 合并.xlsx
只依赖 openpyxl。
"""
import glob, argparse
from openpyxl import load_workbook, Workbook

def to_num(v):
    """文本型数字清洗:'3,000'/'¥3,000' -> 3000,真文本返回 None。"""
    if isinstance(v, str):
        s = v.replace(',', '').replace(',','').replace('¥','').replace('¥','').strip()
        try: return float(s)
        except ValueError: return None
    return v

def merge(pattern, out):
    header = None; data = {}
    for f in sorted(glob.glob(pattern)):
        wb = load_workbook(f, data_only=True)
        for name in wb.sheetnames:
            rows = list(wb[name].values)
            if not rows: continue
            h = [str(c) if c is not None else '' for c in rows[0]]
            if header is None: header = h
            if h != header:
                raise SystemExit(f"表头不一致: {f}[{name}] {h} vs {header} —— 先统一表头再跑")
            for r in rows[1:]:
                if r[0] is None: continue
                key = str(r[0])
                data.setdefault(key, [0]*(len(header)-1))
                for i in range(1, len(header)):
                    v = to_num(r[i] if i < len(r) else None)
                    if isinstance(v, (int, float)): data[key][i-1] += v
    if header is None: raise SystemExit("没匹配到任何文件")
    wb = Workbook(); ws = wb.active; ws.title = "合并"
    ws.append(header)
    for k in sorted(data): ws.append([k] + data[k])
    wb.save(out); print("saved", out, len(data), "人")

if __name__ == "__main__":
    p = argparse.ArgumentParser(); p.add_argument("pattern"); p.add_argument("out")
    a = p.parse_args(); merge(a.pattern, a.out)

实测输出

造了 3 份测试表:技术部(王喵 3000/3200/3100、李汪)、市场部(王喵 500/600/700、赵汪)、外包部(王喵工资故意存成文本 '3000'),再加一份表头写成「名字」的坏表:

$ python3 merge_salary.py 'src/*.xlsx' 合并.xlsx
saved 合并.xlsx 3 人

$ python3 -c "from openpyxl import load_workbook; [print(r) for r in load_workbook('合并.xlsx').active.values]"
('姓名', '3月', '4月', '5月')
('李汪', 2800, 2800, 3000)
('王喵', 6500, 7000, 6900)
('赵汪', 1500, 1500, 1600)

核对「王喵」:3 月 = 3000 + 500 + 3000(文本清洗后) = 6500 ✓;4 月 = 3200 + 600 + 3200 = 7000 ✓;5 月 = 3100 + 700 + 3100 = 6900 ✓。跨簿同名行求和、文本型数字清洗全部正确。

喂入表头不一致的坏表:

表头不一致: bad/坏表.xlsx[Sheet] ['名字', '3月', ...] vs ['姓名', '3月', ...] —— 先统一表头再跑

明确指出是哪个文件的哪列对不上,而不是悄悄丢掉一整个部门。

不想自己跑脚本?喵窝资源铺提供人工代跑+交付服务(miaowoo.900890.xyz):把你的多份工资/流水表打包发来,说明按哪列合并,脚本适配你的真实表头后交付合并结果。由 AI 完成脚本与初验、人工复核后交付,9.9 元起。虚拟物品,交付后不退款,介意勿拍。

← 返回喵窝资源铺 · 相关:批量合并Excel工作簿 · 相关:考勤时长列清洗算工资