1. 目录

  • 1. 目录
  • 2. 背景
  • 3. 设计
    • 3.1. 分析
    • 3.2. 设计
  • 4. 环境
  • 5. 实现
    • 5.1. 核心
    • 5.2. 定义实体
    • 5.3. 数据库操作

2. 背景

很多地方需要用到 解析地理区域 这块数据,但是人家是一个网页。

2016 年我用 Java 实现了一版,感觉使用起来不是很方便,后来又在 2020 年 用 Python 又实现了一次,代码量明显少了很大一部分。

3. 设计

3.1. 分析

地理区域的数据结构比较经典,完全是树形数据结构,随着树的深度增大,数据增长在10倍左右。

  • 根节点的数量是 1
  • 省/直辖市 数量为 33
  • 城市 354
  • 区县 3331

3.2. 设计

  • 在实现上,使用了 Pythonpymysql 的类库。

  • 在数据存储上,利用了数据库,在数据库选择上,本着简单易用的原则选择了 MySQL ,数据库表设计如下:


CREATE TABLE areas (  code VARCHAR(30) DEFAULT NULL,name VARCHAR(100) DEFAULT NULL,lv INT(11) DEFAULT NULL,sup_code VARCHAR(30) DEFAULT NULL,flag VARCHAR(6) DEFAULT NULL,url VARCHAR(60) DEFAULT NULL
) ;
  • 因为涉及到对数据库的操作,我这里按照设计思想,对数据库的操作,都封装在一个 DbUtil.py
  • 数据大的过程中,需要考虑分页

4. 环境

  • Pycharm 2021.2.2
  • Python 3.10
  • Anaconda3
插件 说明
pymysql
BeautifulSoup

通过 Python Interpreter 安装 或者 pip 安装都可以,自行视实际情况自行选择

5. 实现

AreaMa.py 中,依次执行 init_province()init_city()init_county()init_town()init_village()

  • init_province(): 第一级 省|直辖市
  • init_city(): 第二级 地级市
  • init_county(): 第三级 区县
  • init_town(): 第四级 乡镇街道
  • init_village(): 第四级 自然村/社区

5.1. 核心


import urllib.requestfrom bs4 import BeautifulSoup
from sip.Area import Area
from util.DbUtil import *G_HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'}G_URL = r'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2021/index.html'
""" 获取URL前缀 """
G_URL_PREFIX = G_URL[0:G_URL.rindex('/') + 1]'''
爬取国家统计局网站中 关于行政区划的数据,按照层级进行编辑,最终形成一个树形结构。
包含:省/直辖市/自治区、地级市、区县、乡/镇/街道、居委会/村'''def main():""" 第一级 省|直辖市 """# init_province()""" 第二级 地级市 """# init_city()""" 第三级 区县 """# init_county()""" 第四级 乡镇街道 """init_town()""" 第四级 自然村/社区 """# init_village()if __name__ == '__main__':main()""" Init 执行入口,按照 1、2、3层级顺序执行 """def get_by_lv(lv):with UsingMysql(commit=True) as um:um.cursor.execute(f'select code,name,lv,sup_code,url from areas where lv=%s', lv)data = um.cursor.fetchall()return data""" 由于街道数量量比较大,所以要分页处理 """def init_village():do_init_village(4)def do_init_village(lv):pag = UsingMysql(numPerPage=20)sql = r'select code,name,lv,sup_code,url from areas where lv=%s'param = lvfor ret in pag.queryForList(sql, param):paramsList = DataToJson(ret)get_village(paramsList)""" 由于街道数量量比较大,所以要分页处理 """def init_town():do_init_town(3)# print(get_by_lv(1))'''
code,name,lv,sup_code,url
'''def DataToJson(data):jsonData = []for row in data:result = {}result['code'] = row[0]result['name'] = row[1]result['lv'] = row[2]result['sup_code'] = row[3]result['url'] = row[4]jsonData.append(result)return jsonDatadef do_init_town(lv):pag = UsingMysql(numPerPage=20)sql = r'select code,name,lv,sup_code,url from areas where lv=%s'param = lvfor ret in pag.queryForList(sql, param):paramsList = DataToJson(ret)# print(len(paramsList))get_town(paramsList)'''
批量插入数据库
'''def batch_insert(_list):with UsingMysql(commit=True) as um:um.cursor.executemany(""" INSERT INTO areas (code,name,lv,sup_code,url) VALUES (%s,%s,%s,%s,%s)""",_list)def splicing(paramsList):_list = list()for el in paramsList:_list.append((el.code, el.name, el.lv, el.sup_code, el.url))batch_insert(_list)'''
数据字典,维护 层级对应的 DIV标签
'''
G_LV_DICT = {1: 'provincetr', 2: 'citytr', 3: 'countytr', 4: 'towntr', 5: 'villagetr'}'''
核心方法 通用处理方法,封装处理解析逻辑
'''def commons(_list, lv):all_area = []'''利用父级来遍历子级'''for lp in _list:_url = lp['url']if _url is None or len(_url) == 0:continueelse:req = urllib.request.Request(url=get_to_url(_url, lp['code'], lv), headers=G_HEADERS)res = urllib.request.urlopen(req)html = res.read()soup = BeautifulSoup(html, 'html.parser', from_encoding='gb2312')all_tr = soup.find_all('tr', attrs={'class': G_LV_DICT[lv]}, limit=30)for row in all_tr:if lv == 5:""" 社区的元素发生变更,需要特殊处理"""a_ary = row.find_all('td')all_area.append(Area(None, a_ary[2].get_text(), a_ary[0].get_text(), lp['code'], lv))else:a_ary = row.find_all('a')if len(a_ary):all_area.append(Area(a_ary[0].get('href'), a_ary[1].get_text(), a_ary[0].get_text(), lp['code'], lv))else:aa_ary = row.find_all('td')all_area.append(Area(None, aa_ary[1].get_text(), aa_ary[0].get_text(), lp['code'], lv))'''每次批量写入'''splicing(all_area)'''写完后 置空'''all_area = []'''
性化处理URL地址,并根据层级获取对应URL
'''def get_to_url(url, code, lv):urs = {2: G_URL_PREFIX + url,3: G_URL_PREFIX + url,4: G_URL_PREFIX + code[0:2] + '/' + url,5: G_URL_PREFIX + code[0:2] + '/' + code[2:4] + '/' + url,}return urs.get(lv, None)'''
省/自治区
'''def init_province():all_area = []req = urllib.request.Request(url=G_URL, headers=G_HEADERS)res = urllib.request.urlopen(req)html = res.read()soup = BeautifulSoup(html, 'html.parser', from_encoding='gb2312')all_tr = soup.find_all('tr', attrs={'class': 'provincetr'}, limit=10)for row in all_tr:for r_td in row.find_all('a'):ars = Area(r_td.get('href'), r_td.get_text(), r_td.get('href')[0:2], '0', 1)all_area.append(ars)splicing(all_area)'''
市
'''def init_city():_list = get_by_lv(1)commons(_list, 2)'''
区县
'''def init_county():_list = get_by_lv(2)commons(_list, 3)'''
街道
'''def get_town(paramsList):return commons(paramsList, 4)'''
居委会
'''def get_village(paramsList):return commons(paramsList, 5)

5.2. 定义实体

名称 说明
name 名称
code 编码
url 地址
sup_code 上级编码
lv 层级

class Area:"Area class 行政区域实例"def __init__(self, url, name, code, sup_code, lv):self.name = nameself.code = codeself.url = urlself.sup_code = sup_codeself.lv = lvdef __str__(self) -> str:return 'URL:%s\t  NAME:%s\t  Code:%s\t SUPER_CODE:%s\t LV:%s\t' % (self.url, self.name, self.code, self.sup_code, self.lv)

5.3. 数据库操作


import pymysql
import math""" 用pymysql 操作数据库 """def get_connection():host = '127.0.0.1'port = 13306db = 'area'user = 'root'password = 'xxx'conn = pymysql.connect(host=host, port=port, db=db, user=user, password=password)return conndef initClientEncode(conn):'''mysql client encoding=utf8'''curs = conn.cursor()curs.execute("SET NAMES utf8")conn.commit()return curs""" 使用 with 的方式来优化代码 """class UsingMysql(object):def __init__(self, commit=True, log_label=' In total', numPerPage=20):""":param commit: 是否在最后提交事务(设置为False的时候方便单元测试):param log_label:  自定义log的文字"""self._commit = commitself._log_label = log_labelself.numPerPage = numPerPagedef queryForList(self, sql, param=None):totalPageNum = self.__calTotalPages(sql, param)for pageIndex in range(totalPageNum):yield self.__queryEachPage(sql, pageIndex, param)def __createPaginaionQuerySql(self, sql, currentPageIndex):startIndex = self.__calStartIndex(currentPageIndex)qSql = r'select * from (%s) total_table limit %s,%s' % (sql, startIndex, self.numPerPage)return qSqldef __queryEachPage(self, sql, currentPageIndex, param=None):curs = initClientEncode(get_connection())qSql = self.__createPaginaionQuerySql(sql, currentPageIndex)if param is None:curs.execute(qSql)else:curs.execute(qSql, param)result = curs.fetchall()curs.close()return resultdef __calTotalRowsNum(self, sql, param=None):''' 计算总行数 '''tSql = r'select count(*) from (%s) total_table' % sqlcurs = initClientEncode(get_connection())if param is None:curs.execute(tSql)else:curs.execute(tSql, param)result = curs.fetchone()curs.close()totalRowsNum = 0if result != None:totalRowsNum = int(result[0])return totalRowsNumdef __calTotalPages(self, sql, param):''' 计算总页数 '''totalRowsNum = self.__calTotalRowsNum(sql, param)totalPages = 0tempTotal = totalRowsNum / self.numPerPageif (totalRowsNum % self.numPerPage) == 0:totalPages = tempTotalelse:totalPages = math.ceil(tempTotal)return totalPagesdef __calStartIndex(self, currentPageIndex):startIndex = currentPageIndex * self.numPerPagereturn startIndex;def __calLastIndex(self, totalRows, totalPages, currentPageIndex):'''计算结束时候的索引'''lastIndex = 0if totalRows < self.numPerPage:lastIndex = totalRowselif ((totalRows % self.numPerPage == 0)or (totalRows % self.numPerPage != 0 and currentPageIndex < totalPages)):lastIndex = currentPageIndex * self.numPerPageelif (totalRows % self.numPerPage != 0 and currentPageIndex == totalPages):  # 最后一页lastIndex = totalRowsreturn lastIndexdef __enter__(self):""" 在进入的时候自动获取连接和cursor """conn = get_connection()cursor = conn.cursor(pymysql.cursors.DictCursor)conn.autocommit = Falseself._conn = connself._cursor = cursorreturn selfdef __exit__(self, *exc_info):""" 提交事务 """if self._commit:self._conn.commit()""" 在退出的时候自动关闭连接和cursor """self._cursor.close()self._conn.close()@propertydef cursor(self):return self._cursor

Python-20:解析行政区域Python版相关推荐

  1. 【python PDF解析】python 读取PDF文件内容

    一.问题描述 利用python,去读取pdf文本内容. 二.效果 三.运行环境 python2.7 四.需要安装的库 pip install pdfminer 五.实现源代码 代码1(win64) # ...

  2. python分片操作_【python原理解析】python中分片的实现原理及使用技巧

    首先:说明什么是序列? 序列中的每一个元素都会被分配一个序号,即元素的位置,也称为索引:在python中的序列包含:字符串.列表和元组 然后是:什么是分片? 分片就是通过操作索引访问及获得序列的一个或 ...

  3. 了解女友的心还不如了解Python之在Python中解析和修改XML

    2021年12月15日 10:14 ·  阅读 30 摘要: 工作中我们时常需要解析用不同语言编写的数据.Python 提供了许多库来解析或拆分用其他语言编写的数据.在这篇 Python XML 解析 ...

  4. python列表解析的新方法

    python 列表解析我感觉是python非常灵活的一个地方,一开始接触它的时候,特别是之前学过其它的语言, 你会感觉很不习惯,怎么看怎么不对劲,老是觉的哪个地方怪怪的,这就是列表解析的魔力所在. p ...

  5. 面试官问我:如何在 Python 中解析和修改 XML

    摘要:我们经常需要解析用不同语言编写的数据.Python提供了许多库来解析或拆分用其他语言编写的数据.在此 Python XML 解析器教程中,您将学习如何使用 Python 解析 XML. 本文分享 ...

  6. python html解析_Python HTML解析器

    python html解析 Python html.parser module provides us with the HTMLParser class, which can be sub-clas ...

  7. Python 常见的 170 道面试题全解析:2022 版

    Python 常见的 170 道面试题全解析:2019 版 语言特性 1.谈谈对 Python 和其他语言的区别 答:Python 是一门语法简洁优美,功能强大无比,应用领域非常广泛,具有强大完备的第 ...

  8. 时间语义解析工具 Python版,从文本中提取时间,并解析其含义,在线使用,时间语义识别

    时常我们需要从文本中,提取出时间信息,并将这个信息标准化,例如: [新华社报2021-9-9]国家统计局今天发布了2021年8月份全国CPI(居民消费价格指数) 需要从中抽取出 2021-9-9 和 ...

  9. python爬取图片全网通_荣耀20 PRO现货发售 华为官方解析何为Python爬虫

    荣耀20 PRO现货发售 华为官方解析何为Python爬虫 2019-07-08 10:51:56 0点赞 1收藏 0评论 7月8日,荣耀手机官微宣布,荣耀20 PRO现已全面开放购.荣耀20 PRO ...

最新文章

  1. 传感器数据完善 AI 功能,激起机器人“网络效应”
  2. 中国九章量子计算机诞生!比最快的超算快一百万亿倍
  3. [leetcode] 22. Generate Parentheses(medium)
  4. 在html里面动画变颜色,html – 在悬停时填充文本颜色动画,带有动画颜色
  5. 关于c++静态成员函数
  6. 06.十分钟学会表达式语言EL
  7. 高计能计算要实现软着陆
  8. node 将汉字转化为拼音
  9. MySQL安装错误: unknown option '--skip-federated'
  10. zoj 3488 conic section
  11. Seafile 1.3 发布,文件同步和协作平台
  12. 超详细简单解决git的上传和下载
  13. R语言高级算法之人工神经网络(Artificial Neural Network)
  14. vue v-for循环的用法
  15. python shell 运行时不打印日志_shell编程
  16. 小D课堂 - 零基础入门SpringBoot2.X到实战_第4节 Springboot2.0单元测试进阶实战和自定义异常处理_20、SpringBoot2.x配置全局异常实战...
  17. 【从零开始学架构-李运华】05|复杂度来源:高可用
  18. 如何用Excel快速制作甘特图?(超详细!)
  19. 我的世界服务器物品定价,我的世界商店特性官方解答 创作者决定最初定价
  20. Python学习日记04

热门文章

  1. 读 The Algorithmic Foundations of Differential Privacy(一)
  2. 【Java】Java的垃圾回收机制小结
  3. 机会从来都是留给有准备的人,当然,也总是留给那些耐得住寂寞的人, 在别人玩的时候,静下心来学习
  4. vue路由缓存keepAlive导致echarts图表100px偶现
  5. 2021-08-08ctf中的上传upload题目.user.ini绕过后缀黑名单过滤(同文件夹下有php文件突破口)
  6. CVE-2022-33891:Apache Spark 命令注入漏洞通告
  7. 强势出击!兑现诺言,完成FTP远程备份数据完美批处理
  8. 500万AI人才缺口!教育部新增高职人工智能专业
  9. 铁氧体磁珠相关知识总结
  10. Exception in thread “main“ java.lang.UnsupportedClassVersionError: org/apache