批量核验身份证号是否有效:第18位校验码原理 + 30行Python

2026-10-06 · 喵窝资源铺

收表的人都知道:表格里的身份证号总有几个是随手乱填的。位数不够的、出生日期是13月32日的、末位数字对不上的——人工抽查几百行根本看不过来。这篇讲清楚身份证号的内部结构,给一个 30 行的 Python 脚本批量核验,能揪出三类错误。文中代码实跑验证,真实输出贴在下面。

身份证号的结构

位置内容
第1–6位地址码(省市区),只影响归属地,不参与有效性判断
第7–14位出生日期 YYYYMMDD,必须是真实存在的日期
第15–17位顺序码(同地区同日生人编号,奇男偶女)
第18位校验码:由前17位加权求和取模算出,可为数字或 X

第18位校验码怎么算的

前17位各乘一个加权因子后求和,对 11 取模,从字符表里查对应字符:

W = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
M = "10X98765432"
校验码 = M[sum(位 × 权) % 11]

这套机制意味着:随便编一个号,有约 10/11 的概率第18位对不上——校验码就是专门防乱填的。反向也成立:知道前17位可以反推出合法的第18位(测试脚本时我们就这么造测试数据,确保算法自洽)。

脚本全文(idv.py,实跑通过)

# -*- coding: utf-8 -*-
"""批量核验18位身份证号是否合法(格式/出生日期/校验码)。
用法: python3 idv.py 号1 号2 号3 ...   或配合 csv 逐行调用
"""
import re, sys, datetime

W = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
M = "10X98765432"

def check(idn):
    idn = idn.strip().upper()          # 末位小写 x 统一转大写
    if not re.fullmatch(r'\d{17}[\dX]', idn):
        return "格式错误:应为18位,末位可为X"
    y, m, d = int(idn[6:10]), int(idn[10:12]), int(idn[12:14])
    try:
        datetime.date(y, m, d)
    except ValueError:
        return f"出生日期非法:{y}-{m:02d}-{d:02d}"
    s = sum(int(c)*w for c, w in zip(idn[:17], W))
    if M[s % 11] != idn[17]:
        return f"校验位错误:应为 {M[s % 11]}"
    return "OK"

for t in sys.argv[1:]:
    print(t, "->", check(t))

实跑输出(真实运行结果)

$ python3 idv.py 110101199003077774 11010519491231002X 11010119900307777X 110101199003077 110101199013077772
110101199003077774 -> OK
11010519491231002X -> OK
11010119900307777X -> 校验位错误:应为 4
110101199003077   -> 格式错误:应为18位,末位可为X
110101199013077772 -> 出生日期非法:1990-13-07

五条测试数据覆盖五类情况:合法号 OK、末位为X的合法号 OK、错校验位、短号、13月非法日期,全部判定正确。另用「前17位反推校验位」验证了算法自洽:对测试号算出应为 4,与脚本判定一致。

实跑抓到的坑

一个诚实的边界

这个脚本校验的是号码本身是否合法(自洽),不等于「这个号真的属于某个人」——合法结构的号也可以是编出来的。要核验人证一致性必须走官方实名认证接口。它适合的场景是:收表时先过滤掉明显乱填的行,减少人工复核量。

表格收多了?喵窝资源铺提供代清洗服务
批量身份证号/手机号/日期格式核验与清洗,¥19.9 起,脚本实跑验证后交付。
去看五档服务 →

更多实跑干货:工具与文章合集|需要现成的批量清洗脚本,直接下单喵。