行政区划信息抽取算法(区划抽取)

前言

  • 行政区划信息抽取算法,简称区划抽取

  • 本文区划仅针对中国(缺港澳台,2020年国家统计局更新,2019年数据):
    http://www.stats.gov.cn/tjsj/tjbz/
    http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/index.html

必备常识

  • 行政区划:是行政区域划分的简称,是国家为了进行分级管理而实行的区域划分。
等级详情(2019年,每年会有更新,以更新为准,不要参考此处)合计
省级行政区23个省、5个自治区、4个直辖市、2个特别行政区34
地级行政区293个地级市、7个地区、30个自治州、3个盟333
县级行政区965个市辖区、387个县级市、1323个县、117个自治县、49个旗、3个自治旗、1个特区、1个林区2846
乡级行政区8516个街道、20975个镇、8122个乡、966个民族乡、153个苏木、1个民族苏木、1个县辖区38734
  • 省级行政区划
区划代码(前2位)省级行政区简称行政中心区划代码(前2位)省级行政区简称行政中心
11北京市北京43湖南省长沙
12天津市天津44广东省广州
13河北省石家庄45广西壮族自治区南宁
14山西省太原46海南省海口
15内蒙古自治区内蒙古呼和浩特50重庆市重庆
21辽宁省沈阳51四川省川、蜀成都
22吉林省长春52贵州省贵、黔贵阳
23黑龙江省哈尔滨53云南省云、滇昆明
31上海市上海54西藏自治区拉萨
32江苏省南京61陕西省陕、秦西安
33浙江省杭州62甘肃省甘、陇兰州
34安徽省合肥63青海省西宁
35福建省福州64宁夏回族自治区银川
36江西省南昌65新疆维吾尔自治区乌鲁木齐
37山东省济南71台湾省台北
41河南省郑州81香港特别行政区香港
42湖北省武汉82澳门特别行政区澳门

极简代码示例

from jieba.posseg import dt  # 带词性分词

dt.tokenizer.add_word('禅城区', 9, 'ns')  # 地名词库加载

text = '''
广东省佛山市南海区桂城街道地处南海区东南部,西部与禅城区祖庙街道、南部与禅城区石湾镇街道、顺德区陈村镇接壤
'''.strip()

print(set(i.word for i in dt.cut(text)if i.flag == 'ns'))  # 带词性分词
print
{‘顺德区’, ‘佛山市’, ‘南海区’, ‘广东省’, ‘桂城’, ‘禅城区’, ‘石湾镇’}

特殊数据处理

歧义名称

不知所云的名称,直接抛弃

exclusion = {'市辖区', '县', '省直辖县级行政区划'}

例如【郊区】一词有歧义,可用【阳泉市郊区、佳木斯市郊区、铜陵市郊区】来替代

exclusion = {'城区', '郊区', '矿区', '东区', '西区'}

重名

  • 省级和市级的行政区划没有重名,区级和县级的行政区划有重名,遇到重名要进一步从文本中查找其上级区划来消除歧义

消歧方法1:层级查找(荐给Java用)

消歧方法2:根据区划代码前缀 设置权重(荐给Python用)

1
0.4
0.6
0.4
0.4
贵阳市的白云区
贵阳市
白云区
5201
520113
440111
5201
520113
440111
1.4
0.8
0.6
from jieba.posseg import dt  # 带词性分词
name2code = {'白云区': {'440111': .6, '520113': .4}, '广州市': {'4401': 1}, '贵阳市': {'5201': 1}}
dt.tokenizer.add_word('白云区', tag='ns')

def similarity(a, b):
    """
    佛山市 match 南海区 = 50%
    禅城区 match 南海区 = 0%
    """
    if a == b:
        return 0
    la, lb, score = len(a), len(b), 0
    for i in range(min(la, lb)):
        if a[i] == b[i]:
            score += 1
        else:
            break
    return score / (la + lb)

def extract(text):
    # 识别区划名称,取集合(或计数器)
    s = set(i.word for i in dt.cut(text)if i.flag == 'ns')
    # 区划名称转区划代码
    ls = [(code, score) for name in s for code, score in name2code[name].items()]
    # 权值计算并排序
    ls = sorted([(c1, sum(similarity(c1, c2) for c2, s2 in ls)+s1) for c1, s1 in ls], key=lambda x: x[1], reverse=True)
    # 返回最长区划码
    return max(ls, key=lambda x: len(x[0]))[0]

text1 = '广州市的白云区'
text2 = '贵阳市的白云区'
print(text1, extract(text1))
print(text2, extract(text2))
print
广州市的白云区 440111
贵阳市的白云区 520113

后缀

有时有些区划不使用全称而使用简称,例如:佛山(佛山市)、宁夏(宁夏回族自治区)等,此时就要识别简称来获取区划代码

后缀名称

suffix = [
    '特别行政区', '民族苏木', '自治区', '自治州', '自治县', '自治旗', '民族乡', '地区',
    '特区', '林区', '街道', '苏木', '省', '市', '盟', '区', '县', '旗', '镇', '乡']

去掉后缀

PROVINCE_ABBR={'北京':'北京市','山西':'山西省','云南':'云南省','河北':'河北省','西藏':'西藏自治区','宁夏':'宁夏回族自治区',
'安徽':'安徽省','重庆':'重庆市','青海':'青海省','山东':'山东省','湖南':'湖南省','江西':'江西省','广西':'广西壮族自治区',
'甘肃':'甘肃省','陕西':'陕西省','海南':'海南省','黑龙江':'黑龙江省','上海':'上海市','辽宁':'辽宁省','吉林':'吉林省',
'贵州':'贵州省','福建':'福建省','江苏':'江苏省','广东':'广东省','内蒙古':'内蒙古自治区','河南':'河南省','四川':'四川省',
'浙江':'浙江省','天津':'天津市','新疆':'新疆维吾尔自治区','湖北':'湖北省'}

_CITY_ABBR={'武威','荆门','宿州','亳州','洛阳','邵阳','郑州','眉山','赤峰','阜新','岳阳','衡水','贵阳','榆林','聊城','南平',
'枣庄','随州','泰州','长春','梅州','百色','淮南','白银','珠海','内江','铜川','丹东','三亚','商洛','滨州','株洲','绥化',
'宁波','钦州','扬州','沈阳','新余','宣城','铁岭','许昌','潮州','昌都','舟山','双鸭山','松原','阳泉','鸡西','徐州','白山',
'赣州','鹤岗','黄冈','阜阳','东营','遵义','焦作','桂林','承德','海口','西宁','荆州','昆明','林芝','宜宾','汕尾','韶关',
'咸宁','河池','延安','南昌','益阳','宿迁','巴彦淖尔','葫芦岛','吐鲁番','惠州','开封','银川','营口','芜湖','阳江','漯河',
'伊春','黄山','湘潭','朔州','濮阳','牡丹江','张家界','萍乡','宁德','烟台','黑河','大庆','临沂','太原','云浮','绵阳','庆阳',
'汕头','那曲','哈密','杭州','衢州','广州','锦州','南通','镇江','嘉兴','宜昌','鹰潭','广安','乌鲁木齐','毕节','酒泉','铜仁',
'青岛','玉林','周口','天水','唐山','七台河','吕梁','济南','石家庄','深圳','柳州','德阳','衡阳','拉萨','无锡','襄阳','自贡',
'大连','丽水','九江','来宾','日照','安顺','西安','威海','孝感','景德镇','临沧','河源','陇南','池州','秦皇岛','滁州','保定',
'盐城','定西','铜陵','厦门','常德','鞍山','白城','贵港','合肥','资阳','信阳','金昌','菏泽','南阳','邯郸','茂名','南充',
'呼伦贝尔','上饶','金华','吉安','台州','中卫','曲靖','雅安','张掖','广元','朝阳','黄石','鄂州','张家口','佳木斯','渭南',
'克拉玛依','汉中','龙岩','巴中','湖州','肇庆','永州','莆田','常州','泰安','沧州','平顶山','新乡','六盘水','十堰','长治',
'海东','南京','清远','辽源','安康','鹤壁','佛山','吴忠','石嘴山','潍坊','忻州','邢台','包头','济宁','遂宁','呼和浩特',
'温州','兰州','晋城','晋中','四平','淮安','泉州','江门','娄底','三门峡','贺州','崇左','乐山','通化','通辽','马鞍山','德州',
'平凉','吉林','宜春','揭阳','梧州','山南','辽阳','安阳','苏州','东莞','乌海','郴州','南宁','防城港','玉溪','昭通','盘锦',
'六安','安庆','淄博','抚顺','武汉','嘉峪关','攀枝花','廊坊','鄂尔多斯','漳州','本溪','连云港','泸州','固原','绍兴','中山',
'蚌埠','达州','福州','商丘','三沙','齐齐哈尔','怀化','丽江','抚州','湛江','日喀则','临汾','咸阳','长沙','运城','淮北',
'大同','宝鸡','哈尔滨','成都','乌兰察布','普洱','北海','三明','儋州','保山','驻马店'}
CITY_ABBR={i:i+'市'for i in _CITY_ABBR}

去后缀的坑

白云机场不在白云区,而在花都区
黄埔大道不在黄浦区,而在天河区
海珠广场不在海珠区,而在越秀区

数据下载

直接下载

2019年数据(2020年出)下载地址(CSDN资源,免积分,下载好了点个赞哈😓):
https://download.csdn.net/download/Yellow_python/12469089

使用爬虫

直接复制可用,存excel,需要半天时间

request

import requests
from random import choice
from time import strftime, sleep

UA = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
    'Mozilla/5.0(compatible;MSIE9.0;WindowsNT6.1;Trident/5.0;',
    'Mozilla/4.0(compatible;MSIE8.0;WindowsNT6.0;Trident/4.0)',
]

red = lambda x: print('\033[91m', x, '\033[0m', sep='')
yellow = lambda x: print('\033[93m', x, '\033[0m', sep='')


def write_log(*args):
    with open(strftime('%Y%m%d')+'.txt', 'a', encoding='utf-8') as f:
        for a in args:
            red(a)
            f.write('{}\n'.format(a))
        f.write('\n')
    sleep(1)  # 减速


def get(url, encode='gb2312', times=50):
    if times < 0:
        write_log(url, times)
        return ''
    try:
        r = requests.get(url, headers={'User-Agent': choice(UA)}, timeout=30)
    except Exception as e:
        write_log(url, e, times)
        return get(url, encode, times - 1)
    if r.status_code == 200:
        r.encoding = encode
        return r.text
    else:
        write_log(url, r.status_code, times)
        return get(url, encode, times - 1)


if __name__ == '__main__':
    print(get('http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/', 'utf-8'))

spider

"""
国家统计局:统计用区划和城乡划分代码

http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/index.html  1级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/11.html  2级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/11/1101.html  3级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/11/01/110101.html  4级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/11/01/01/110101001.html  5级

http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/index.html  1级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/44.html  2级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/44/4419.html  3级
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/44/19/441900003.html  5级
"""
from re import findall
from request import *

# 主页,按年份调整
HOMEPAGE = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/index.html'
# 网址前缀
PREFIX = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/'

# 数据存储列表
ls_of_ls = []


def lv5(url):
    """乡级"""
    html = get(url)
    # yellow(html)
    for code, other, name in findall("<td>([0-9]{12})</td><td>([0-9]{3})</td><td>([\u4e00-\u9fa5]+)</td>", html):
        print(code, name, other)
        ls_of_ls.append([code, name, other])


def lv4(prefix):
    """县级 -> 乡级"""
    html = get(prefix)
    # yellow(html)
    prefix = prefix[:-11]
    for suffix, code, name in findall("<a href='([0-9]{2}/([0-9]{6,9})[.]html)'>([\u4e00-\u9fa5]+)</a>", html):
        url = prefix + suffix
        print(code, name, url)
        ls_of_ls.append([code, name, url])
        lv5(url)


def lv3(prefix):
    """地级 -> 县级or乡级"""
    html = get(prefix)
    # yellow(html)
    prefix = prefix[:-9]
    for suffix, code, name in findall("<a href='([0-9]{2}/([0-9]{6,9})[.]html)'>([\u4e00-\u9fa5]+)</a>", html):
        url = prefix + suffix
        print(code, name, url)
        ls_of_ls.append([code, name, url])
        if len(code) == 6:
            lv4(url)
        elif len(code) == 9:
            lv5(url)
        else:
            write_log(code, name, url)


def lv2(url):
    """省级 -> 地级"""
    html = get(url)
    # yellow(html)
    for suffix, code, name in findall("<a href='([0-9]{2}/([0-9]{4})[.]html)'>([\u4e00-\u9fa5]+)</a>", html):
        url = PREFIX + suffix
        print(code, name, url)
        ls_of_ls.append([code, name, url])
        lv3(url)


def lv1():
    """省级"""
    html = get(HOMEPAGE)
    # yellow(html)
    for suffix, code, name in findall("<a href='(([0-9]{2})[.]html)'>([\u4e00-\u9fa5]+)[^\u4e00-\u9fa5]*?</a>", html):
        url = PREFIX + suffix
        print(code, name, url)
        ls_of_ls.append([code, name, url])
        lv2(url)


def run():
    try:
        lv1()
    except Exception as e:
        write_log(e)
        with open('region.txt', 'w', encoding='utf-8') as f:
            for ls in ls_of_ls:
                f.write(','.join(ls) + '\n')
    from pandas import DataFrame
    DataFrame(ls_of_ls, columns=['code', 'name', 'others']).to_excel('region.xlsx', index=False)


if __name__ == '__main__':
    # lv3('http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/44/4406.html')  # 佛山
    # lv3('http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/44/4419.html')  # 东莞
    run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小基基o_O

您的鼓励是我创作的巨大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值