客户名单散落在几十个Excel里,手机号有的带横线(138-1234-5678)、有的带空格、有的是纯数字,还要混在备注里。手工整理到崩溃?下面这份脚本一次搞定:提取 → 清洗 → 校验 → 去重 → 统计来源次数。
import openpyxl, re, glob
# 负向断言:左右都不是数字,防止从长订单号里"截"出假手机号
PAT = re.compile(r'(? 出现次数
for p in sorted(glob.glob('客户表/*.xlsx')):
wb = openpyxl.load_workbook(p, data_only=True)
for ws in wb: # 每个sheet都要扫
for row in ws.iter_rows(values_only=True):
for cell in row:
if cell is None: continue
for m in PAT.findall(str(cell)):
n = norm(m)
if n: seen[n] = seen.get(n, 0) + 1
# 导出
out = openpyxl.Workbook(); ows = out.active
ows.append(["手机号", "出现次数"])
for k, c in seen.items(): ows.append([k, c])
out.save('手机号清单.xlsx')
测试数据:5行客户表(3种格式混用)+ 1个含13位长数字串的边角文件。
138-1234-5678 、13912345678、13812345678、13800000000、13900001111(前两个归一化后是同一号码,正确合并)1238123456789 未误报——负向断言把整串挡住了如果写成 1[3-9]\d{9} 直接 findall,遇到13位订单号 1238123456789 不会命中(第2位是2),但遇到 91381234567800 这类串就会从中间抠出 13812345678——把订单号的碎片当成客户号码群发短信,事故级别。必须加 (?。
138-1234-5678 和 13812345678 是同一个客户,但字符串不同。先 re.sub(r'\D','',s) 再校验长度与号段,去重才成立。校验放归一化之后,否则带横线的合法号码会被长度检查误杀。
如果手机号是 =VLOOKUP(...) 拼出来的,不带 data_only=True 时 openpyxl 给你的是公式字符串——正则一个号码都提不到,还以为是正则写错了。参数加上后读的是上次Excel保存时的缓存值。
号码整理出来只是第一步:要不要按来源文件标注归属、要不要顺带保留客户姓名对应行,都是一行代码的扩展。需要按 姓名-手机号-备注 整表合并去重的,看这篇《多份Excel客户名单合并去重》。
懒得自己跑?把表格发给我们代做:提取/清洗/去重/合并都可以,看服务列表,¥9.9起。
本文所有代码均为实跑验证后发布,测试数据可复现。虚拟服务交付后不退款;未达承诺免费重做。