从身份证号码中获取性别、出生日期、籍贯,并更新mongodb
有这样的需求,人员信息是存在mongodb中,需要存放人员的身份证、性别、出生日期、籍贯等信息。通过脚本导入这些信息,但是只导入了身份证号码,其他信息空缺。现在需要补全其他信息。
其实身份证信息就包含了性别、出生日期、籍贯信息。公民身份号码是特征组合码,由前十七位数字本体码和最后一位数字校验码组成。排列顺序从左至右依次为六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
地址码: 表示编码对象常住户口所在县(市、旗、区)的行政区划代码。
出生日期码:表示编码对象出生的年、月、日,年、月、日代码之间不用分隔符,格式为YYYYMMDD,如19880328。
顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
校验码: 根据本体码,通过采用ISO 7064:1983,MOD 11-2校验码系统计算出校验码。前面有提到数字校验码,我们知道校验码也有X的,实质上为罗马字符X,相当于10.
实现思路很简单了,就是得到所有有身份证号码等纪录,解析身份证号码,获取性别、出生日期、籍贯信息,再更新mongodb。
想到了两种技术方案解决:python脚本和JavaScript。
1、python脚本
python需要使用pymongo,用于操作mongodb,需要引入。python代码直接在终端执行就行。python test.py
1 # encoding=utf-8 2 3 ''' 4 Created on 2017年12月22日 5 mongodb中存放人像信息,有身份证号码,无性别、籍贯、出生日期,需要根据mongodb中身份证字段,获取性别、籍贯、出生日期,更新到mongodb中 6 7 @author: tim 8 ''' 9 10 import re 11 from pymongo import MongoClient #需要引入pymongo,用于连接mongodb 12 from datetime import datetime 13 14 logPath = "/Users/tim/updatemongo/log/log.txt" #日志 15 16 #身份证前两位代表的省市,作为籍贯 17 area = {"11":"北京", "12":"天津", "13":"河北", "14":"山西", "15":"内蒙古", "21":"辽宁", "22":"吉林", "23":"黑龙江", "31":"上海", "32":"江苏", "33":"浙江", "34":"安徽", "35":"福建", "36":"江西", "37":"山东", "41":"河南", "42":"湖北", "43":"湖南", "44":"广东", "45":"广西", "46":"海南", "50":"重庆", "51":"四川", "52":"贵州", "53":"云南", "54":"西藏", "61":"陕西", "62":"甘肃", "63":"青海", "64":"宁夏", "65":"新疆", "71":"台湾", "81":"香港", "82":"澳门", "91":"国外"} 18 19 20 # 身份证有效性校验 21 def checkIdcard(idcard): 22 Messages = ['验证通过!', '身份证号码位数不对!', '身份证号码出生日期超出范围或含有非法字符!', '身份证号码校验错误!', '身份证地区非法!'] 23 24 idcard = str(idcard) #身份证号码转成字符串 25 idcard = idcard.strip() #移除字符串头尾指定的字符(默认为空格) 26 idcard_list = list(idcard) #转成列表 27 28 # 地区校验 29 if(not area[(idcard)[0:2]]): 30 return Messages[4] 31 32 # 15位身份号码检测 33 if(len(idcard) == 15): 34 if((int(idcard[6:8]) + 1900) % 4 == 0 or((int(idcard[6:8]) + 1900) % 100 == 0 and (int(idcard[6:8]) + 1900) % 4 == 0)): 35 ereg = re.compile('[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$') 36 else: 37 ereg = re.compile('[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$') 38 39 # 测试出生日期的合法性 40 if(re.match(ereg, idcard)): 41 return Messages[0] 42 else: 43 return Messages[2] 44 45 # 18位身份号码检测 46 elif(len(idcard) == 18): 47 # 出生日期的合法性检查 48 # 闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9])) 49 # 平年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8])) 50 if(int(idcard[6:10]) % 4 == 0 or (int(idcard[6:10]) % 100 == 0 and int(idcard[6:10]) % 4 == 0)): 51 ereg = re.compile('[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$') # //闰年出生日期的合法性正则表达式 52 else: 53 ereg = re.compile('[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$') # //平年出生日期的合法性正则表达式 54 55 # 测试出生日期的合法性 56 if(re.match(ereg, idcard)): 57 # 计算校验位 58 S = (int(idcard_list[0]) + int(idcard_list[10])) * 7 + (int(idcard_list[1]) + int(idcard_list[11])) * 9 + (int(idcard_list[2]) + int(idcard_list[12])) * 10 + (int(idcard_list[3]) + int(idcard_list[13])) * 5 + (int(idcard_list[4]) + int(idcard_list[14])) * 8 + (int(idcard_list[5]) + int(idcard_list[15])) * 4 + (int(idcard_list[6]) + int(idcard_list[16])) * 2 + int(idcard_list[7]) * 1 + int(idcard_list[8]) * 6 + int(idcard_list[9]) * 3 59 Y = S % 11 60 M = "F" 61 JYM = "10X98765432" 62 M = JYM[Y] # 判断校验位 63 if(M == idcard_list[17]): # 检测ID的校验位 64 return Messages[0] 65 else: 66 return Messages[3] 67 else: 68 return Messages[2] 69 else: 70 return Messages[1] 71 72 73 # 获取身份证信息 74 def getIdInfo(idcard): 75 checkresult = checkIdcard(idcard) 76 if checkresult == '验证通过!': 77 idcard = str(idcard) 78 idcard = idcard.strip() 79 ID_address = (idcard)[0:2] # 前两位是籍贯 80 ID_sex = idcard[14:17] # 14位之后表示性别 81 82 if(len(idcard) == 15): 83 ID_birth = '19' + idcard[6:12] # 获取15位身份证出生年月日,补全19 84 else: 85 ID_birth = idcard[6:14] # 获取18位身份证出生年月日 86 87 strAddress = area[ID_address] # 籍贯 88 strGender = 'male' # 性别 89 90 year = ID_birth[0:4] 91 moon = ID_birth[4:6] 92 day = ID_birth[6:8] 93 strBirthDate = year + '-' + moon + '-' + day # 出生日期 94 95 if int(ID_sex) % 2 == 0: 96 strGender = 'female' 97 else: 98 strGender = 'male' 99 100 # print '籍贯', strAddress 101 # print '性别', strGender 102 # print '出生日期', strBirthDate 103 104 idInfo = [strAddress, strGender, strBirthDate] 105 106 return idInfo 107 else: 108 writeLog("异常:"+idcard+checkresult) 109 110 print idcard, checkresult 111 return 0; 112 113 114 # 读取mongodb身份证信息,更新mongodb 115 # serverIp mongodb服务器IP 116 # port mongodb服务器端口 117 # dbName 要连接的数据库名称 118 # collectionName 数据库集合名称 119 def updateMongoInfo(serverIp, port, dbName, collectionName): 120 client = MongoClient(serverIp, port) # 连接mongodb数据库 121 db = client[dbName] # 指定数据库名称 122 collection = db[collectionName] # 获取集合名 123 124 log = open(logPath, 'a') 125 log.writelines([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ",开始更新……", "\n"]) 126 print datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ",开始更新……", "\n" 127 128 # 查找身份证号码不为空的记录,才执行 129 for i in collection.find({'id_card':{"$ne":""}}): 130 result = getIdInfo(i['id_card']) 131 if(result != 0): 132 log.writelines([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ",更新,", i['idcard'], result[0], result[1], result[2], "\n"]) 133 print i['id_card'], result 134 collection.update({"id_card":i['id_card']}, {"$set":{"address":result[0], "gender":result[1], "birth_date":result[2]}}) 135 136 log.writelines([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ",更新结束……", "\n"]) 137 log.close() 138 print datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ",更新结束……", "\n" 139 140 141 # 写日志 142 def writeLog(content): 143 with open(logPath, 'w+') as log: 144 log.writelines([datetime.now().strftime('%Y-%m-%d %H:%M:%S'), " ", content, "\n"]) 145 146 147 # 入口函数 148 def start(logPath,serverIp, port, dbName, collectionName): 149 logPath=logPath 150 updateMongoInfo(serverIp, port, dbName, collectionName) 151 152 153 # 测试 154 start('/Users/tim/updatemongo/log/log.txt','127.0.0.1', 27017, 'tim', 'person') 155
2、javascript代码
mongodb是支持Javascript代码的,见我的另一篇博客:Mongodb常用操作
1 //---------------------------------------------------------- 2 // 功能:检查身份证号码 3 // 参数:idcard 4 // 返回值:检验信息 5 //---------------------------------------------------------- 6 function checkIdCard(idcard){ 7 var Errors=new Array( 8 "验证通过!", 9 "身份证号码位数不对!", 10 "身份证号码出生日期超出范围或含有非法字符!", 11 "身份证号码校验错误!", 12 "身份证地区非法!" 13 ); 14 15 var area={11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江", 16 31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东", 17 41:"河南",42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏", 18 61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}; 19 20 var idcard,Y,JYM; 21 var S,M; 22 var idcard_array = new Array(); 23 idcard_array = idcard.split(""); 24 //地区检验 25 if(area[parseInt(idcard.substr(0,2))]==null) return Errors[4]; 26 //身份号码位数及格式检验 27 switch(idcard.length){ 28 case 15: 29 if ( (parseInt(idcard.substr(6,2))+1900) % 4 == 0 || ((parseInt(idcard.substr(6,2))+1900) % 100 == 0 && 30 (parseInt(idcard.substr(6,2))+1900) % 4 == 0 )){ 31 ereg=/^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$/;//测试出生日期的合法性 32 } else { 33 ereg=/^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$/;//测试出生日期的合法性 34 } 35 if(ereg.test(idcard)) return Errors[0]; 36 else return Errors[2]; 37 38 break; 39 case 18: 40 //18位身份号码检测 41 //出生日期的合法性检查 42 //闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9])) 43 //平年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8])) 44 if ( parseInt(idcard.substr(6,4)) % 4 == 0 || (parseInt(idcard.substr(6,4)) % 100 == 0 && 45 parseInt(idcard.substr(6,4))%4 == 0 )){ 46 ereg=/^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$/;//闰年出生日期的合法性正则表达式 47 } else { 48 ereg=/^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$/;//平年出生日期的合法性正则表达式 49 } 50 if(ereg.test(idcard)){//测试出生日期的合法性 51 //计算校验位 52 S = (parseInt(idcard_array[0]) + parseInt(idcard_array[10])) * 7 53 + (parseInt(idcard_array[1]) + parseInt(idcard_array[11])) * 9 54 + (parseInt(idcard_array[2]) + parseInt(idcard_array[12])) * 10 55 + (parseInt(idcard_array[3]) + parseInt(idcard_array[13])) * 5 56 + (parseInt(idcard_array[4]) + parseInt(idcard_array[14])) * 8 57 + (parseInt(idcard_array[5]) + parseInt(idcard_array[15])) * 4 58 + (parseInt(idcard_array[6]) + parseInt(idcard_array[16])) * 2 59 + parseInt(idcard_array[7]) * 1 60 + parseInt(idcard_array[8]) * 6 61 + parseInt(idcard_array[9]) * 3 ; 62 Y = S % 11; 63 M = "F"; 64 JYM = "10X98765432"; 65 M = JYM.substr(Y,1);//判断校验位 66 if(M == idcard_array[17]) return Errors[0]; //检测ID的校验位 67 else return Errors[3]; 68 } 69 else return Errors[2]; 70 break; 71 default: 72 return Errors[1]; 73 break; 74 } 75 76 } 77 78 //---------------------------------------------------------- 79 // 功能:根据身份证号获得出生日期 80 // 参数:身份证号 psidno 81 // 返回值:出生日期 82 //---------------------------------------------------------- 83 function getBirthday(psidno){ 84 var birthdayno,birthdaytemp; 85 if(psidno.length==18){ 86 birthdayno=psidno.substring(6,14); 87 }else if(psidno.length==15){ 88 birthdaytemp=psidno.substring(6,12); 89 birthdayno="19"+birthdaytemp; 90 }else{ 91 print("错误的身份证号码,请核对!"); 92 return false; 93 } 94 95 var birthday=birthdayno.substring(0,4)+"-"+birthdayno.substring(4,6)+"-"+birthdayno.substring(6,8); 96 return birthday; 97 } 98 99 //---------------------------------------------------------- 100 // 功能:根据身份证号获得性别 101 // 参数:身份证号 psidno 102 // 返回值:性别 103 //---------------------------------------------------------- 104 function getGender(psidno){ 105 var sexno,sex; 106 if(psidno.length==18){ 107 sexno=psidno.substring(16,17); 108 }else if(psidno.length==15){ 109 sexno=psidno.substring(14,15); 110 }else{ 111 print(psidno+"错误的身份证号码,请核对!"); 112 return false; 113 } 114 var tempid=sexno%2; 115 if(tempid==0){ 116 sex='female'; 117 }else{ 118 sex='male'; 119 } 120 121 return sex; 122 } 123 124 //---------------------------------------------------------- 125 // 功能:获取籍贯 126 // 参数:身份证号 psidno 127 // 返回值:籍贯 128 function getProvinceNameByIdNo(idcard) { 129 var area = { 11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古", 130 21: "辽宁", 22: "吉林", 23: "黑龙江", 31: "上海", 32: "江苏", 131 33: "浙江", 34: "安徽", 35: "福建", 36: "江西", 37: "山东", 41: "河南", 42: "湖北", 132 43: "湖南", 44: "广东", 45: "广西", 133 46: "海南", 50: "重庆", 51: "四川", 52: "贵州", 53: "云南", 54: "西藏", 61: "陕西", 134 62: "甘肃", 63: "青海", 64: "宁夏", 135 65: "新疆", 71: "台湾", 81: "香港", 82: "澳门", 91: "国外" 136 }; 137 138 var provinceName = ""; 139 var provinceNo = idcard.substr(0, 2); 140 if (area[parseInt(provinceNo)] != null) { 141 provinceName = area[parseInt(provinceNo)]; 142 } 143 144 return provinceName; 145 } 146 147 //去掉空格 148 function trim(s) { 149 return s.replace(/^\s+|\s+$/g, ""); 150 } 151 152 //入口函数 153 //查询mongodb,根据身份证号码,先检验身份证,再提取性别、出生日期、籍贯,再更新mongodb 154 function start(collectionName){ 155 var cursor = db.getCollection(collectionName).find({'id_card':{'$ne':''}});//查询身份证号码不为空的集合 156 while (cursor.hasNext()) { 157 var strCardId=cursor.next()["id_card"]; 158 var card_id=trim(strCardId);//去掉空格后 159 160 if (card_id.length > 0) { 161 var checkResult=checkIdCard(card_id); 162 if (checkResult!='验证通过!') { 163 print(strCardId+checkResult); 164 continue; 165 } 166 else { 167 var birthday = getBirthday(card_id); 168 var provinceName = getProvinceNameByIdNo(card_id); 169 var gender=getGender(card_id); 170 print(strCardId+":"+birthday+","+provinceName+","+gender); 171 172 //更新mongodb 173 //这里的id_card使用原始的,避免原始的身份证号码带空格,update找不到记录 174 db.getCollection(collectionName).updateMany({"id_card":strCardId}, {"$set":{"address":provinceName, "gender":gender, "birth_date":birthday}}); 175 } 176 } 177 } 178 } 179 180 //入口,传入集合名称 181 start('person');