全国省市县三级地区数据库设计&基于jsoup获取最新数据
设计了适用于三级地址选择的数据库,并通过jsoup爬取相关数据
·
一、引言
为了实现地址选择等功能,经常需要获取详细地区信息,本文面向这一目标设计了数据库,并通过jsoup爬虫爬取最新数据。
二、数据库设计
数据库结构,此处较为简单,就保存了每个区域的层级、名称及其上级区域的代码,以便查询层级关系。
-- ----------------------------
-- Table structure for district
-- ----------------------------
DROP TABLE IF EXISTS `district`;
CREATE TABLE `district` (
`_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
`region_name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL COMMENT '地区名称',
`region_short_name` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL COMMENT '地区简称',
`region_code` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL COMMENT '行政地区编号',
`region_level` tinyint(3) UNSIGNED NOT NULL COMMENT '地区级别 1-省、自治区、直辖市 2-地级市、地区、自治州、盟 3-市辖区、县级市、县',
`region_parent_id` int(10) UNSIGNED NOT NULL COMMENT '所属上级地区编号',
PRIMARY KEY (`_id`) USING BTREE
);
三、获取数据并生成导入脚本
在民政网站可以查询全国地区信息,地址:县以上行政区划代码
可以发现数据具有极强的顺序性规律:所有地域都紧邻其上级地区之后,即所有市都在其所属省份之后而不穿插其他省份,所有县都在其所属市之后而不穿插其他市。这为我们之后的数据处理提供了极大的方便。
爬取前,先通过F12查看网页结构,可以发现:省、市地区的class为xl722830,而县一级的class为xl732830,不能通过一个类名获取所有信息,因此本文采取的方法为遍历所有tr获取地区代码和名称。为将数据插入库中,同时输出了sql脚本。代码如下:
public class Districter {
public static void main(String[] args) {
try {
//2020年12月中华人民共和国县以上行政区划代码网页
Document doc = Jsoup.connect("http://www.mca.gov.cn/article/sj/xzqh/2020/20201201.html").get();
Element eDiv = doc.getElementById("2020年12月份县以上行政区划代码_28320");
Element tBody = eDiv.getElementsByTag("tbody").get(0);
Elements trs = tBody.getElementsByTag("tr");
// 以上为爬取部分,trs保存所有地域数据的行,
// 处理trs获取地区代码和名称
List<String> codes = new ArrayList<String>();
List<String> names = new ArrayList<String>();
for (int i = 3; i < trs.size()-9; i++) { // 跳过不属于地区信息的tr
Elements info = trs.get(i).getElementsByTag("td");
String code = info.get(1).text().trim();
String name = info.get(2).text().trim();
codes.add(code);
names.add(name);
}
// 判断数据正确性
assert codes.size()== names.size():"地区代码与地区名称数目不相等!!";
processDistricts(codes, names);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 设置标记判断直辖市等特殊情况,一次遍历处理地区数据
* @param codeList
* @param nameList
* @throws IOException
*/
private static void processDistricts(List<String> codeList, List<String> nameList) throws IOException {
// 输出sql脚本
String filePath="path/to/script";
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
int num = codeList.size();
// x、y分别保存i之前的省、市级地区的下标,即当前市县所属于的上级地区
int x=0,y=0;
// 标记一级地区(省、直辖市等)下是否有二级地区(市)
boolean has2=false;
String code;
for (int i = 0; i < num; i++) {
code=codeList.get(i);
if (code.endsWith("0000")){
System.out.println(codeList.get(i) +"\t"+nameList.get(i)+",1级");
writer.write("insert into district(region_name,region_code,region_level,region_parent_id) values('"
+nameList.get(i)+"','"+codeList.get(i)+"',1,'0');"
+"\n");
has2=false;
x=i;
}else if (code.endsWith("00")){
System.out.println(codeList.get(i) + "\t" + nameList.get(i) + "\t" + "2级,属于" + nameList.get(x));
writer.write("insert into district(region_name,region_code,region_level,region_parent_id) values('"
+nameList.get(i)+"','"+codeList.get(i)+"',2,'"+codeList.get(x)+"');"+"\n");
has2=true;
y=i;
}else {
if (has2){
System.out.println(codeList.get(i) + "\t" + nameList.get(i) + "\t" + "3级,属于:" + nameList.get(y));
writer.write("insert into district(region_name,region_code,region_level,region_parent_id) values('"
+nameList.get(i)+"','"+codeList.get(i)+"',3,'"+codeList.get(y)+"');"
+"\n");
}else {
System.out.println(codeList.get(i) + "\t" + nameList.get(i) + "\t" + "3级,属于:" + nameList.get(x));
writer.write("insert into district(region_name,region_code,region_level,region_parent_id) values('"
+nameList.get(i)+"','"+codeList.get(i)+"',3,'"+codeList.get(x)+"');"
+"\n");
}
}
}
writer.close();
}
}
四、使用
基本的SQL语句就好。
1.查看所有一级地区:
select * from district where region_parent_id='0';
2.查看北京市各下级地区:
select * from district where region_parent_id='110000';
3.搜索陕西省下属地区
select * from district where region_parent_id=(select region_code from district where region_name='陕西省');
更多推荐
已为社区贡献1条内容
所有评论(0)