爬取国家统计局2020年五级联动行政区划(精确)

无其他新鲜数据的情况下,这篇应该是国家统计局专栏的最后一篇
思路和之前爬国家统计局运用的根节点叶节点思路基本相同,先放代码,具体的说明想好再解释~(代码中有部分注释)
爬到最小的村级大概用时一个半小时(因为没用代理ip或者多进程,最后要访问40000+乡级网页比较耗时),总共630000+村级数据,但是网上的数据量分布在640000-740000之间,先不说和我数据不符,竟然体量也不尽相同,我也不知道是怎么回事qaq
(最新:看到这位博主的博客https://blog.csdn.net/xuemu2008/article/details/110262257,他的数据条目和这篇代码实现能完成的数目完全一致,所以应该是完全正确的)

import requests
import re
import xlsxwriter
import time
time_start=time.time()
agent={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'}
choose_ls=[depth*2 if depth<=3 else 3*(depth-1) for depth in range(1,6)]#根据深度大小取12位代码前**位
match_level=['provincetr','citytr','countytr','towntr','villagetr']
initurl='http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2020/index.html'
total_dict={}
depth=0
each_root={initurl:('','')}
max_depth=5#可选,1-5分别表示省级、地级、县级、乡级、村级,进而爬取固定深度范围内所有的叶节点以及该深度下的根节点
while depth<max_depth:
    total_count=0
    next_root={}
    for url in each_root:
        code_join=each_root[url][0]+'-' if depth!=0 else each_root[url][0]
        zone_join=each_root[url][1]+'-' if depth!=0 else each_root[url][1]
        change_root='/'.join(url.split('/')[:-1])+'/'
        while True:
            try:
                req=requests.get(url,headers=agent)
                req.encoding='GBK'#中文解码,不要用req.encoding=req.apparent_encoding,这样识别出来的req.encoding='gb2312',有好多复杂汉字解不出码
                text=req.text
                text=text.replace('\n','\\n')#正则表达式会跳过换行符(无法识别下一行),因此将换行符替换
                special_sigh=False
                if match_level[depth] in text:
                    match_text=re.findall(r"class='%s'>(.*?)</table"%match_level[depth],text)[0]
                    break
                else:
                    search=False
                    for level in range(depth,5):#东莞、中山、儋州缺县级单位,因此需要进行识别并放入下一节点存储
                        if match_level[level] in text:
                            match_text=re.findall(r"class='%s'>(.*?)</table"%match_level[level],text)[0]
                            search=True
                            special_sigh=True
                            print('特殊区划:%s'%each_root[url][1])
                            break
                    if search:
                        break
                    else:
                        print('服务器繁忙')
                        time.sleep(2)
            except:
                print('服务器繁忙')
                time.sleep(2)
        if special_sigh:
            next_root[url]=(code_join,zone_join)
        else:
            if depth!=0:
                has_tree=re.findall(r"href='(.*?)'>(\d+?)<.*?html'>(.*?)</a></td></tr>",match_text)
            else:
                base_tree=re.findall(r"href='(.*?)'>(.*?)<br/",match_text)
                has_tree=[(each[0],each[0].split('.html')[0],each[1]) for each in base_tree]
            base_no=re.findall(r"td>(\d+?)</td><td>(.*?)</td></tr>",match_text)
            no_tree=[(each[0],re.findall(r'<td>(.+)',each[1])[0] if 'td' in each[1] else each[1]) for each in base_no]
            for each in has_tree:
                each_dir=change_root+each[0]
                next_root[each_dir]=(code_join+each[1][:choose_ls[depth]],zone_join+each[2])
                if depth==3:
                    if (total_count+1)%100==0:
                        print('已爬取%d个,在路径%s处'%(total_count+1,zone_join+each[2]))
                else:
                    print('在路径%s处'%(zone_join+each[2]))
            if no_tree:
                for each in no_tree:
                    total_dict[code_join+each[0][:choose_ls[depth]]]=zone_join+each[1]
                    if depth==4:
                        if (total_count+1)%800==0:
                            print('已爬取%d个,在路径%s处'%(total_count+1,zone_join+each[1]))
                    else:
                        print('已获取路径%s'%(zone_join+each[1]))
        total_count+=1
    depth+=1
    each_root=next_root
def decompose(each):
    if type(total_dict[each])==tuple:
        codelist=total_dict[each][0].split('-')
        namelist=total_dict[each][1].split('-')
    else:
        codelist=each.split('-')
        namelist=total_dict[each].split('-')
    if len(codelist)<depth:
        for i in range(len(codelist),depth):
            codelist.append('')
            namelist.append('')
    ziplist=list(zip(codelist,namelist))
    return [i for j in ziplist for i in j]
sort_name=['省级','地级','县级','乡级','村级']
real_column=[(sort_name[each]+'代码',sort_name[each]+'名称') for each in range(depth)]
flat_col=[i for each in real_column for i in each]
total_dict.update(each_root)
if depth<=3:#县级及以上数据量不大(约三千行),可以用excel存储
    wk=xlsxwriter.Workbook('五级联动.xlsx')
    sh=wk.add_worksheet('sheet1')
    for each in range(2*depth):
        sh.write(0,each,flat_col[each])
    totalrow=1
    for each in total_dict:
        flatlist=decompose(each)
        for i in range(2*depth):
            sh.write(totalrow,i,flatlist[i])
        totalrow+=1
    wk.close()
else:#县级往下数据较多,excel没有优势,因此写入csv存储
    book=open('五级联动.csv','w',encoding='utf-8')
    book.write(','.join(flat_col)+'\n')
    for each in total_dict:
        flatten=decompose(each)
        book.write(','.join(flatten)+'\n')
    book.close()
time_end=time.time()
rest_second=time_end-time_start
print('用时%d分%d秒'%divmod(rest_second,60))

村级经pandas sort_values排序后,如图所示:
在这里插入图片描述

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
在 miniui 中展示行政区划联动需要使用 miniui 的 ComboBox 组件,同时结合行政区划数据进行展示。 以下是一个简单的示例代码,可以供参考: ```html <!DOCTYPE html> <html> <head> <title>miniui 行政区划联动展示</title> <meta charset="utf-8" /> </head> <body> <div id="province"></div> <div id="city"></div> <div id="district"></div> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> <script src="https://cdn.bootcss.com/miniui/3.10.0/miniui.js"></script> <link href="https://cdn.bootcss.com/miniui/3.10.0/themes/default/miniui.css" rel="stylesheet" /> <script> // 省份数据 var provinceData = [ { id: 1, name: "北京市" }, { id: 2, name: "上海市" }, { id: 3, name: "广东省" } ]; // 城市数据 var cityData = { 1: [ { id: 11, name: "北京市" } ], 2: [ { id: 21, name: "上海市" } ], 3: [ { id: 31, name: "广州市" }, { id: 32, name: "深圳市" }, { id: 33, name: "珠海市" } ] }; // 区县数据 var districtData = { 11: [ { id: 1101, name: "东城区" }, { id: 1102, name: "西城区" }, { id: 1103, name: "崇文区" }, { id: 1104, name: "宣武区" } ], 21: [ { id: 2101, name: "黄浦区" }, { id: 2102, name: "卢湾区" }, { id: 2103, name: "徐汇区" }, { id: 2104, name: "长宁区" } ], 31: [ { id: 3101, name: "天河区" }, { id: 3102, name: "海珠区" }, { id: 3103, name: "番禺区" }, { id: 3104, name: "花都区" } ], 32: [ { id: 3201, name: "福田区" }, { id: 3202, name: "罗湖区" }, { id: 3203, name: "南山区" }, { id: 3204, name: "宝安区" } ], 33: [ { id: 3301, name: "香洲区" }, { id: 3302, name: "斗门区" }, { id: 3303, name: "金湾区" } ] }; // 初始化省份下拉框 var provinceCombo = mini.get("province"); provinceCombo.setData(provinceData); // 初始化城市下拉框 var cityCombo = mini.get("city"); cityCombo.setData(cityData[provinceCombo.getValue()]); // 初始化区县下拉框 var districtCombo = mini.get("district"); districtCombo.setData(districtData[cityCombo.getValue()]); // 省份下拉框值改变事件 provinceCombo.on("valuechanged", function () { cityCombo.setData(cityData[provinceCombo.getValue()]); cityCombo.select(0); districtCombo.setData(districtData[cityCombo.getValue()]); districtCombo.select(0); }); // 城市下拉框值改变事件 cityCombo.on("valuechanged", function () { districtCombo.setData(districtData[cityCombo.getValue()]); districtCombo.select(0); }); </script> </body> </html> ``` 在上述代码中,我们首先定义了三个下拉框,分别用于展示省份、城市和区县。接着,我们定义了省份、城市和区县的数据,这些数据可以从后端获取,也可以直接在前端定义。 在代码中,我们使用 `mini.get` 方法获取到了省份下拉框、城市下拉框和区县下拉框,并将对应的数据通过 `setData` 方法设置到了下拉框中。同时,我们为省份下拉框和城市下拉框分别绑定了 `valuechanged` 事件,当省份下拉框的值改变时,会自动更新城市下拉框和区县下拉框的数据。城市下拉框的值改变时,会自动更新区县下拉框的数据。 运行上述代码,即可看到行政区划联动的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kappuccinno

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值