2026-10-06 · 喵窝资源铺
每月发工资条的经典流程:打开总表 → 筛选技术部 → 复制粘贴出新文件 → 再筛选销售部 → ……几十个部门手工分一遍,还每人再做一份「工资条」。这篇给一个 只依赖 openpyxl 的脚本,一次跑完两件事:
文中代码实跑验证,真实输出贴在下面。
| 情况 | 处理方式 |
|---|---|
| 姓名是纯空格(" ") | 该行有其他数据 → 进「跳过脏行」报告,不静默丢 |
| 整行空白 | 静默跳过(不算异常) |
| 部门为空 / 纯空格 | 归入「未填写部门」单独成表,缺部门的能一眼找到 |
部门名带 / 等非法文件名字符 | 替换成 _ 再存文件 |
| 列顺序被 HR 改过 | 按表头名找列,不写死列号 |
构造了一份 9 行数据的工资总表(故意埋了纯空格姓名、空部门、整行空行),运行后真实输出:
源文件: 工资总表.xlsx 有效数据行: 7 部门数: 4 [后勤部] 1人: 周九 [技术部] 2人: 张三, 李四 [未填写部门] 1人: 钱七 [销售部] 3人: 王五, 赵六, 孙八 跳过的脏行: 第7行: 姓名为空但该行有其他数据
生成的文件:
按部门拆分/后勤部.xlsx 技术部.xlsx 未填写部门.xlsx 销售部.xlsx
工资条/后勤部_周九.xlsx 技术部_张三.xlsx 技术部_李四.xlsx
未填写部门_钱七.xlsx 销售部_孙八.xlsx 销售部_王五.xlsx 销售部_赵六.xlsx
抽查验证:张三工资条内容行 = [张三, 技术部, 10800, 9600, 2026-10](应发/实发/月份全对);技术部拆分表里张三、李四两行数据逐列正确。连跑两次所有输出文件 md5 完全一致(幂等,可放进定时任务放心重跑)。
第一版里写的是:
out.save(f"工资条/{dept}_{name}".replace('/', '_') + ".xlsx")
看着没问题,结果 replace 作用在整个路径字符串上:工资条/技术部_张三.xlsx 被替换成 工资条_技术部_张三.xlsx——目录形同虚设,7 份工资条全平铺在根目录,还因为目录没建直接报错。正确写法是只对文件名做替换:
out.save(os.path.join("工资条", f"{dept}_{name}".replace('/', '_') + ".xlsx"))
用部门名拼文件名时这是必踩的坑:部门叫 销售/华东,不处理就是一个带斜杠的非法文件名。
存为 split_payslips.py,和工资总表放同一目录运行:
python split_payslips.py
输入表要求有 5 列:姓名、部门、应发工资、实发工资、发放月份(其他列如工号/基本工资/社保会一并复制进部门表;列顺序随意)。输出 拆分报告.txt 记录每部门人数和跳过的脏行。
# -*- coding: utf-8 -*-
"""工资总表按部门拆分 + 每人生成独立工资条。只依赖 openpyxl。"""
import openpyxl
from openpyxl.styles import Font
from copy import copy
import os
SRC = "工资总表.xlsx"
wb = openpyxl.load_workbook(SRC)
ws = wb.active
# 按表头名找列(HR 改列顺序也能跑)
headers = {}
for c in range(1, ws.max_column + 1):
v = ws.cell(1, c).value
if v is not None and str(v).strip():
headers[str(v).strip()] = c
need = ["姓名", "部门", "应发工资", "实发工资", "发放月份"]
missing = [n for n in need if n not in headers]
if missing:
raise SystemExit(f"缺少必需列: {missing}")
def cellv(r, col):
v = ws.cell(r, col).value
if v is None or (isinstance(v, str) and not v.strip()):
return None
return v
# 读数据行
rows = []
skipped = []
for r in range(2, ws.max_row + 1):
name = cellv(r, headers["姓名"])
if name is None:
if any(cellv(r, headers[n]) is not None for n in need):
skipped.append((r, "姓名为空但该行有其他数据"))
continue # 整行空行静默跳过
dept = cellv(r, headers["部门"]) or "未填写部门"
rows.append((r, str(name).strip(), str(dept).strip(),
{n: ws.cell(r, headers[n]).value for n in need}))
groups = {}
for item in rows:
groups.setdefault(item[2], []).append(item)
# 1) 每个部门拆出一个表(带格式)
os.makedirs("按部门拆分", exist_ok=True)
for dept, items in sorted(groups.items()):
out = openpyxl.Workbook()
ow = out.active
ow.title = dept[:31]
for c in range(1, ws.max_column + 1):
hc = ow.cell(1, c, ws.cell(1, c).value)
hc.font = copy(ws.cell(1, c).font)
hc.fill = copy(ws.cell(1, c).fill)
i = 2
for rownum, name, _, _ in sorted(items, key=lambda x: x[0]):
for c in range(1, ws.max_column + 1):
oc = ow.cell(i, c, ws.cell(rownum, c).value)
if ws.cell(rownum, c).has_style:
oc.font = copy(ws.cell(rownum, c).font)
oc.number_format = ws.cell(rownum, c).number_format
i += 1
out.save(os.path.join("按部门拆分", dept.replace('/', '_') + ".xlsx"))
# 2) 每人一个工资条文件(项目一行、值一行)
os.makedirs("工资条", exist_ok=True)
pay_cols = [n for n in need if n != "姓名"]
for _r, name, dept, data in sorted(rows, key=lambda x: (x[2], x[1])):
out = openpyxl.Workbook()
ow = out.active
ow.title = "工资条"
ow.cell(1, 1, f"{name} 工资条({dept})").font = Font(bold=True, size=12)
ow.cell(2, 1, "项目").font = Font(bold=True)
ow.cell(3, 1, "金额 / 内容").font = Font(bold=True)
ow.cell(2, 2, "姓名")
ow.cell(3, 2, name)
for j, col in enumerate(pay_cols, start=3):
ow.cell(2, j, col)
ow.cell(3, j, data[col])
for j in range(1, 3 + len(pay_cols)):
ow.column_dimensions[openpyxl.utils.get_column_letter(j)].width = 16
out.save(os.path.join("工资条", f"{dept}_{name}".replace('/', '_') + ".xlsx"))
# 3) 清单报告
lines = [f"源文件: {SRC}", f"有效数据行: {len(rows)}", f"部门数: {len(groups)}"]
for dept, items in sorted(groups.items()):
names = ", ".join(n for _, n, _, _ in sorted(items))
lines.append(f" [{dept}] {len(items)}人: {names}")
if skipped:
lines.append("跳过的脏行:")
for r, why in skipped:
lines.append(f" 第{r}行: {why}")
open("拆分报告.txt", "w").write("\n".join(lines))
print("完成:", len(groups), "个部门表,", len(rows), "份工资条")
不想自己跑脚本?喵窝资源铺提供 Excel/工资条批量处理定制服务,由 AI 助手交付(可加您的真实需求定制列、发邮件分发工资条等),价格透明、说明均为 AI 服务身份。
虚拟物品,一经交付不退款(详见店铺页面说明)。
相关文章:打卡明细自动揪考勤异常 · 两部门工资表按身份证号跨文件对账