(原创)从网页上爬取行政区代码并通过身份证号码获取出生地

证监会近日出台了更为严格的风控监管法规,因而我得到了新的需求——重做风险评测,最后一项是要统计用户的居住地。产品设计是在用户填写之前,按照用户的身份证号码将用户居住地的默认值设为出生地。
实现是比较简单的,身份证前六位依次对应我们的省、市、县的行政代码,将各个行政代码与对应的地址以key-value(Python中的字典)的形式写成常量或者写入数据库,然后拿着身份证前六位查询即可。问题在于,需要先把行政代码拿到手,然后写为字典。

1.爬取行政区代码

先查找到国家最新公布的行政区划代码网站页面

1.png

我们可以拷贝进编辑器,但想要存为字典,就需要一行一行的手动修改格式。虽然也就3000多行吧,但身为一个程序员,哪怕是最初级的,也该有一个偷懒的心,更何况人生苦短我用Python

2.png

下面开始爬取操作,这么一个简单的网页不需要复杂的爬虫,使用urllib2+BeautifulSoup4(py2的搭配,py3中应该为urllib.request+BeautifulSoup3)就可以了
先进入python.exe/ipython.exe

3.png

得到404,说明网站还是做了基本的安全措施,需要加点料。
在Chrome浏览器按F12进入调试模式,按F5刷新,在Network中查看网页中对html的Request,然后拿到Headers中的几个重要的值:Host、User-Agent、Cookie,然后添加到我们自己的request中

4.png
5.png

但这个时候拿到的req并不能直接用,如果print(req)的话,会得到满屏幕的乱码。此时req的内容,是网页的html源代码,等于在网页查看的Request对应的Response。仔细查看Response的内容,终于在第368行找到了我们的目标,拷贝到编辑器中查看

6.png

这时候该BeautifulSoup4出场了,bs4支持多种解析器:Python标准库(“html.parser”)、lxml HTML 解析器(“lxml”)、lxml XML 解析器(“xml”)、html5lib(“html5lib”),并提供了强大的遍历和搜索方法,具体查看文档。

这里使用Python标准库解析器+find_all( name , attrs)搜索标签即可
观察可知行政区编码最里层的标签是<span>, 属性为lang="EN-US"

7.png

下一步就是把110000从"bs4.element.Tag"中取出来,观察发现每条数据的格式都是一样的,倘若可以转为string,然后index('>'),再取偏移6位即可

8.png

如上图所示,一番尝试得到了所有的行政区代码,下一步就是取得对应行政区名称了。
同样是观察可知行政区名称最里层的标签为<span>,属性为style="font-family: 宋体"

此处开始换电脑了,所以截图不一样
9.png

得到的结果是Unicode字符串,但感觉似乎有点不对劲,将areas和codes打印出来对比来看一下,Unicode字符print出来就会显示中文

10.png

发现地址列表中存在空格的情况,再加以处理即可

11.png

现在剩下最后一步了,按字典的格式输出打屏幕上,然后拷贝编辑器中。
话说当时我在这里脑子抽抽了,卡了半个多小时,尝试过直接赋值为字典或者使用codes写入文件再读取出来写成字典,发现地址显示出来的全是Unicode字符,最终在旁人点醒下才跳出思维得到自己要的。

12.png
13.png

想要存入本地文件的话,需要使用codecs这个库,Unicode不能直接写入文件。
完整的代码如下:

import codecs
import urllib2
import sys

from bs4 import BeautifulSoup

url = 'http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html'
headers = {
    'Host': 'www.stats.gov.cn',
    'User-Agent': 'ozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36',
    'Cookie': 'AD_RS_COOKIE=20083363; _gscu_1771678062=957855112bjaz048; _gscs_1771678062=95785511u2vn7148|pv:5; _gscbrs_1771678062=1; _trs_uv=f3pk_6_j35k2nll; _trs_ua_s_1=i5mz_6_j35k2nll',
}

try:
    request = urllib2.Request(url, headers=headers)
    response = urllib2.urlopen(request)
    req = response.read()
except urllib2.URLError, e:
    if hasattr(e, 'code'):
        print(e.code)
    if hasattr(e, 'reason'):
        print(e.reason)
    sys.exit(1)

soup = BeautifulSoup(req, "html.parser")
code_res = soup.find_all('span', attrs={'lang': 'EN-US'})
codes = [str(c)[19:25] for c in code_res]
area_res = soup.find_all('span', attrs={'style': 'font-family: 宋体'})
areas = [a.text.strip() for a in area_res if a.text.strip()]

# 按字典的书写格式输出,避免手动加引号
# for i in range(len(codes)):                       
#    print '    "%s": u"%s",' % (codes[i], areas[i])

with codecs.open('./res.txt', 'w', 'utf-8') as f:
    for i in range(len(codes)):
        f.write(codes[i] + ':' + areas[i] + '\n')

2.通过身份证号码获取用户出生地信息

从网上可以查的18位身份证号码的编排规则,以及最后一位的校验规则

12.png
13.png

如下处理即可

# 一共3508条,不全给出
areacode = {            # 截止2016年7月31日
    "110000": u"北京市",
    "110100": u"市辖区",
    ...
}

# 校验18位身份证号码的合法性
def vertify(id):
    c = 0
    ckeck_codes = '10X98765432'         # 校验码
    for (a, p) in zip(map(int, id[:-1]), range(17, 0, -1)):
        w = (2 ** p) % 11
        c += a * w
    return id[-1] == ckeck_codes[c % 11]

# 获取出生地信息
def area(id):
    province = id[0:2]
    municipal = id[2:4]
    county = id[4:6]
    return (areacode[province + "0000"],
            areacode[province + municipal + "00"],
            areacode[province + municipal + county])

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容