发布于 2026-10-06 · 本文所有数据均为脚本真实运行输出
每个月各收一份客户登记表,三个月下来同一批人重复登记了好几次。要一份干净的名单:每个手机号只留登记日期最新的那条。手工筛一上午,脚本 3 秒。
# -*- coding: utf-8 -*-
"""合并多个月份的 Excel 后按关键列去重,保留最新一条。
用法:python dedup_keep_latest.py [输入目录] [输出xlsx] [关键列] [日期列]"""
import sys, glob, os
import pandas as pd
src = sys.argv[1] if len(sys.argv) > 1 else "."
out = sys.argv[2] if len(sys.argv) > 2 else "dedup.xlsx"
KEY = sys.argv[3] if len(sys.argv) > 3 else "手机号"
DATE = sys.argv[4] if len(sys.argv) > 4 else "登记日期"
frames = []
for f in sorted(glob.glob(os.path.join(src, "*.xlsx"))):
if os.path.basename(f) == os.path.basename(out): # 防止把上次的输出又吃进来
continue
frames.append(pd.read_excel(f))
merged = pd.concat(frames, ignore_index=True)
n0 = len(merged)
merged[DATE] = pd.to_datetime(merged[DATE]) # 日期列转真日期,字符串排序才不出"2026-9-5"排错
merged = (merged.sort_values(DATE)
.drop_duplicates(subset=[KEY], keep="last") # 排序后留最后一条=最新
.sort_values(DATE, kind="stable"))
merged.to_excel(out, index=False)
print(f"{len(frames)} 个文件共 {n0} 行, 按[{KEY}]去重保留[{DATE}]最新 -> {len(merged)} 行 -> {out}")
故意让 13800000001 登记 3 次(1月/2月/3月)、13800000002 登记 2 次,且日期在文件里乱序:
$ python3 dedup_keep_latest.py ./dedup_src ./dedup_src/dedup.xlsx 手机号 登记日期
3 个文件共 6 行, 按[手机号]去重保留[登记日期]最新 -> 3 行 -> ./dedup_src/dedup.xlsx
输出核对(openpyxl 逐行读回):
手机号 姓名 登记日期
13800000003 王五 2026-02-03 ← 只登记过1次,原样保留
13800000001 张三 2026-03-01 ← 登记过3次(1-10/2-20/3-1),留下最新
13800000002 李四 2026-03-08 ← 登记过2次(1-15/3-8),留下最新
每个手机号都精确落在它登记过的最晚日期那一条,输出还按日期排好序。
对同一目录再跑一遍(输出文件也在目录里):
3 个文件共 6 行, 按[手机号]去重保留[登记日期]最新 -> 3 行
还是"3 个文件共 6 行"——脚本认得自己的输出文件并跳过它,不会把 dedup.xlsx 又当输入吃进来变成 9 行、18 行滚雪球。
pd.to_datetime() 转成真日期再排序(实测乱序日期输入照样留对)。os.path.basename(f) == os.path.basename(out) 那行专门挡这个,实测重跑行数不变。keep="last" 保的是"当前顺序的最后一行",不是"日期最新的一行"。顺序错了留的就是最早那条——这正是"去重了但数据不对"的隐形事故。先 sort 再 drop_duplicates,顺序不能反。更多办公自动化实战: 博客合集(50+ 篇) · 服务清单
下单代做 →