文本与CSV敏感信息一键脱敏:手机号/身份证/邮箱/银行卡

发工资表、发客户名单前总要先打码。本脚本一行命令搞定,四类规则可单独开关,文内效果均为实跑输出。

用法

python3 desensitize.py 输入.txt --rules phone,idcard,email,bank

完整脚本

#!/usr/bin/env python3
"""文本/CSV 敏感信息脱敏:手机号、身份证、邮箱、银行卡。规则可关。"""
import re, sys, argparse

PATTERNS = {
    'phone':  (r'(?<!\d)1[3-9]\d{9}(?!\d)',
               lambda m: m.group(0)[:3] + '****' + m.group(0)[7:]),
    'idcard': (r'(?<!\d)\d{17}[\dXx](?!\d)',
               lambda m: m.group(0)[:6] + '********' + m.group(0)[-4:]),
    'email':  (r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}',
               lambda m: m.group(0)[0] + '***' + m.group(0)[m.group(0).index('@'):]),
    'bank':   (r'(?<!\d)(?:\d{4}[ ]?){3,4}\d{2,4}(?!\d)',
               lambda m: m.group(0) if len(re.sub(r'\D','',m.group(0))) > 19  # 20位数字串会被正则填满,误伤长编号
                         else '****' + re.sub(r'\D','',m.group(0))[-4:]),
}

def desensitize(text, rules=None):
    out = text
    for name, (pat, fn) in PATTERNS.items():
        if rules and name not in rules:
            continue
        out = re.sub(pat, fn, out)
    return out

if __name__ == '__main__':
    ap = argparse.ArgumentParser()
    ap.add_argument('file', nargs='?', help='输入文件,缺省读stdin')
    ap.add_argument('--rules', default='phone,idcard,email,bank', help='逗号分隔,如 phone,email')
    a = ap.parse_args()
    src = open(a.file, encoding='utf-8', errors='replace').read() if a.file else sys.stdin.read()
    sys.stdout.write(desensitize(src, set(a.rules.split(','))))

实测效果

输入:联系张三 13812345678,邮箱 [email protected]
输出:联系张三 138****5678,邮箱 z***@qq.com

CSV整文件脱敏输出:
姓名,手机,身份证,邮箱
张三,139****1111,110101********751X,z***@163.com
李四,159****5678,,l***@gmail.com

三个实测坑

坑1:20位纯数字编号被银行卡规则误伤

银行卡正则 (?:\d{4}[ ]?){3,4}\d{2,4} 长度上贪婪通吃,20位纯数字串正好被填满而命中(实测:12345678901234567890 被打成 ****7890)。修法:命中后统计实际数字位数,超过19位原样放行。修后长编号完好。

坑2:身份证正则必须加 (?!\d) 前后边界

实测 "X结尾身份证+3"(11010119900307751X3)不加边界会错位匹配;加 (?<!\d) 和 (?!\d) 后自动跳过紧贴数字的伪证号。

坑3:只打中间段,保留首尾

身份证保留前6后4、手机保留前3后4——脱敏后数据仍可对账去重,这是"可用脱敏"与"不可用打码"的区别。

组合用法

先拆大CSV再逐片脱敏:
for f in part_*.csv; do python3 desensitize.py "$f" > "$f.masked"; done

需要按业务加规则(微信号/工号)或做整列映射脱敏,下单页有19.9元脚本定制档,交付前先在你的样例上跑通。