有一阵子没有更新博客了,来除个草


最近公司运营那边跟我反馈说网站上的省市区信息有点老,有的用户压根找不到自己所在的区域。那么问题来了,这货是住在哪个旮旯里呢?我不得而知。但是数据是零几年的这个毋庸置疑,那么尽快着手办这事是顶要紧的。所以我面临两个选择,1:百度,2:自己找。
虽然我之前不知道这玩意儿应该到哪去找,但,我可以百度啊。
所幸,让我在国家统计局网站上找到了最新的行政区域代码表。

国家统计局官网

但问题是怎么拿下来呢?复制粘贴?不现实,最后表明,大概有三千多行(靠近4K行的样子)除非我疯了。这实在不是一个码农应该有的思维。那么,爬呗,不然呢,国家统计局还能给你JSON文件下载?
按照惯例,爬虫用Python写是比较合适的,当然用Nodejs也可以写,不过很久没写Python了,玩一下。
唠嗑结束。


准备工作:
Python(确认你电脑上装了Python还有pip管理工具,我的版本是2.7.13,如果你的是3.X的就不要往下看了,语法变了)
Mysql(我管你是本地还是线上,得有)
IDE(墙裂推荐Pycharm,来自伟大的JB公司)
OK,现在我们开始装一些外挂

urllib2 抓页面的利器
BeautifulSoup4 解析Dom的利器
pymysql 看名字就知道是数据库操作库

不知道怎么安装的我就提示一下pip install urllib2,如果连pip工具都不会用的话也可以不用往下看了
然后,用urllib2尝试抓一下页面

req = urllib2.Request(url, None, headers)
response = urllib2.urlopen(req)
html = response.read()

基本返回的是,非法访问,特么居然防蜘蛛。没辙,伪装一下header吧。所以urllib2是可以伪装header的,其他的外挂我没研究过,反正Python自带的是不行。

headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}

这段是网上找的,特么也太老了点,换换

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36'}

这段是从我浏览器里拷出来的,还算像点样子
然后顺利爬到html,交给bs4解析

soup = BeautifulSoup(html, "html.parser")

我们看一下页面代码,基本上……卧槽了,无论城市是什么级别的,在html里面都是同一级别的

<p class="MsoNormal"><span style="font-family: 宋体">  </span><span lang="EN-US">110101<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">  东城区</span></p>

就这尿性,如果我没猜错的话又是TRS做的文章管理系统,反正我是挺鄙视这家公司的,技术不咋地,总能接到政府的单子。
没办法,先把数据拿到再整理吧

divs = soup.find_all('p', {'class': 'MsoNormal'})
citys = []
for item in divs:
    code_re = re.compile('<span lang="EN-US">(.*?)<span>')
    code = re.search(code_re, str(item))
    city_re = re.compile(u"([\u4e00-\u9fa5]+)</span>")
    city = re.search(city_re, unicode(item))
    citys.append({"city": urllib2.quote(city.group(1).encode("unicode_escape").decode("unicode_escape").encode("utf-8")), "code": code.group(1)})

这是我的代码,大抵意思就是先从整个页面拿到

标签的内容,然后通过遍历正则出行政区划编码和行政区划名称,我已经不想吐槽Python编码的坑了
现在,数据是进入了citys数组,接下来就对citys数组进行重组
大致思路是:

  1. 遍历出省份,因为省份的规则比较鲜明,6位数,后四位全是0,那么只要需要遍历出后四位全是0的作为省份
  2. 接着就是市级,市级也简单,上级省份的前2位+后两位全是0,那么只要根据这个规则比对出符合的就是市级
  3. 最容易的就是这个区域了,上级市代码的前四位+后两位不是0
    思路理清楚了,做起来就好做了,以下是我的代码
province = []
k1 = 0
for index in range(len(citys)):
    if citys[index]["code"].find("0000") >= 0:
        item_pro = {"area_id": citys[index]["code"], "title": citys[index]["city"], "pid": 0, "son": [], "sort": k1}
        # 再遍历二级城市
        k2 = 0
        for index2 in range(len(citys)):
            if (citys[index2]["code"].find(item_pro["area_id"][:2]) == 0) and (citys[index2]["code"] != item_pro["area_id"]) and (citys[index2]["code"][-2:] == "00"):
                item_city = {"area_id": citys[index2]["code"], "title": citys[index2]["city"], "pid": item_pro["area_id"], "son": [], "sort": k2}
                # 最后是三级城市
                k3 = 0
                for index3 in range(len(citys)):
                    if (citys[index3]["code"].find(item_city["area_id"][:4]) == 0) and (citys[index3]["code"] != item_city["area_id"]):
                        item_area = {"area_id": citys[index3]["code"], "title": citys[index3]["city"], "pid": item_city["area_id"], "sort": k3}
                        item_city["son"].append(item_area)
                        k3 += 1
                item_pro["son"].append(item_city)
                k2 += 1
        province.append(item_pro)
        k1 += 1

这个写的比较繁琐,嵌套三层循环,一言不合就容易死机,我粗算了一下,大概要循环四百多亿次数据,不是一个小工程量。还好Python够快,几秒钟的事情结果就出来了。
数据格式对上了,接下来是要导出为JSON还是直接写入Mysql,就随便啦。

最后附上导出的JSON文件和SQL文件下载地址,还有脚本源码,格式是我自己定的格式,想怎么换就随便了。
2017年最新中国行政区划代码(JSON\SQL\SRC)

Just this.