【Java】实战|省市区三级联动数据爬取

【Java】实战|省市区三级联动数据爬取

前言

  最近收到客服反应,系统的省市区数据好像不准,并且缺了一些地区。经过询问同事得知,数据库内的数据是从老项目拷贝过来的,有些年头了。难怪会缺一些数据。正好最近在对接网商银行,发现网商提供了省市区的数据的接口。这就很舒服了哇,抄起键盘就是干,很快的就把同步程序写好了。

  然后在同步的过程中,发现网商提供的数据和数据库有些对不上。于是默默的打开淘宝京东添加收货地址,看看到底是谁错了。对比到后面发现都有些差异。这就很蛋疼了。看来这个时候谁都不能相信了,只能信国家了。于是我打开了中华人民共和国民政部网站来比对异常的数据。

  对比的过程中,石锤网商数据不准。值得的是表扬淘宝京东已经同步了最新的数据了。但是呢,我并没有找到它们的数据接口。为了修正系统的数据,只能自己爬取了。

锁定爬取目标

【Java】实战|省市区三级联动数据爬取

【Java】实战|省市区三级联动数据爬取

爬取地址如下:

  爬取原理很简单,就是解析HTML元素,然后获取到相应的属性值保存下来就好了。由于使用Java进行开发,所以选用Jsoup来完成这个工作。

<!-- HTML解析器 -->

<dependency>

<groupId>org.jsoup</groupId>

<artifactId>jsoup</artifactId>

<version>1.13.1</version>

</dependency>

网页数据分析

  由于需要解析HTML才能取到数据,所以需要知道数据存储在什么元素上。我们可以打开chrom的控制台,然后选中对应的数据,即可查看存储数据的元素。

【Java】实战|省市区三级联动数据爬取

  通过分析,发现每一行数据都是存储在一个<tr>标签下。我们需要的 区域码区域名称存储在第一和第二个<td>内 。与此同时还要很多空白<td>标签,在编写代码是需要将其过滤掉。

定义基础代码

  先定义好我们的爬取目标,以及Area实体类。

public class AreaSpider{

// 爬取目标

private static final String TARGET = "http://preview.www.mca.gov.cn/article/sj/xzqh/2020/2020/202101041104.html";

@Data

@AllArgsConstructor

private static class Area {

// 区域码

private String code;

// 区域名称

private String name;

// 父级

private String parent;

}

}

爬虫代码编写

public static void main(String[] args) throws IOException{

// 请求网页

Jsoup.connect(TARGET).timeout(10000).get()

// 筛选出 tr 标签

.select("tr")

// 筛选出 tr 下的 td 标签

.forEach(tr -> tr.select("td")

// 过滤 值为空的 td 标签

.stream().filter(td -> StringUtils.isNotBlank(td.text()))

// 输出结果

.forEach(td -> System.out.println(td.text())));

}

解析结果

【Java】实战|省市区三级联动数据爬取

代码优化

  通过上面的代码,我们已经爬取到了页面上的数据。但是并没有达到我们的预期,所以进一步处理将其转换为Area实体。

public static void main(String[] args) throws IOException{

// 请求网页

List<Area> areaList = Jsoup.connect(TARGET).timeout(10000).get()

// 筛选出 tr 标签

.select("tr")

// 筛选出 tr 下的 td 标签

.stream().map(tr -> tr.select("td")

// 过滤 值为空的 td 标签,并转换为 td 列表

.stream().filter(td -> StringUtils.isNotBlank(td.text())).collect(Collectors.toList()))

// 前面提到,区域码和区域名称分别存储在 第一和第二个td,所以过滤掉不符合规范的数据行。

.filter(e -> e.size() == 2)

// 转换为 area 对象

.map(e -> new Area(e.get(0).text(), e.get(1).text(), "0")).collect(Collectors.toList());

// 遍历数据

areaList.forEach(area -> System.out.println(JSONUtil.toJsonStr(area)));

}

解析结果

【Java】实战|省市区三级联动数据爬取

  至此,离我们想要的数据还差了父级区域码 ,我们可以通过区域码计算出来。以河北省为例:河北省:130000石家庄市:130100长安区:130102可以发现规律:0000 结尾是省份,00是市。所以就有了如下代码:

private static String calcParent(String areaCode){

// 省 - 针对第一行特殊处理

if(areaCode.contains("0000") || areaCode.equals("行政区划代码")){

return "0";

// 市

}else if (areaCode.contains("00")) {

return String.valueOf(Integer.parseInt(areaCode) / 10000 * 10000);

// 区

}else {

return String.valueOf(Integer.parseInt(areaCode) / 100 * 100);

}

}

最终代码

public class AreaSpider{

// 爬取目标

private static final String TARGET = "http://preview.www.mca.gov.cn/article/sj/xzqh/2020/2020/202101041104.html";

@Data

@AllArgsConstructor

private static class Area{

// 区域码

private String code;

// 区域名称

private String name;

// 父级

private String parent;

}

public static void main(String[] args) throws IOException{

// 请求网页

List<Area> areaList = Jsoup.connect(TARGET).timeout(10000).get()

// 筛选出 tr 标签

.select("tr")

// 筛选出 tr 下的 td 标签

.stream().map(tr -> tr.select("td")

// 过滤 值为空的 td 标签,并转换为 td 列表

.stream().filter(td -> StringUtils.isNotBlank(td.text())).collect(Collectors.toList()))

// 前面提到,区域码和区域名称分别存储在 第一和第二个td,所以过滤掉不符合规范的数据行。

.filter(e -> e.size() == 2)

// 转换为 area 对象

.map(e -> new Area(e.get(0).text(), e.get(1).text(), calcParent(e.get(0).text()))).collect(Collectors.toList());

// 去除 第一行 "行政区划代码|单位名称"

areaList.remove(0);

areaList.forEach(area -> System.out.println(JSONUtil.toJsonStr(area)));

}

private static String calcParent(String areaCode){

// 省 - 针对第一行特殊处理

if(areaCode.contains("0000") || areaCode.equals("行政区划代码")){

return "0";

// 市

}else if (areaCode.contains("00")) {

return String.valueOf(Integer.parseInt(areaCode) / 10000 * 10000);

// 区

}else {

return String.valueOf(Integer.parseInt(areaCode) / 100 * 100);

}

}

}

数据修正

【Java】实战|省市区三级联动数据爬取

  由于我们需要的是省市区三级数据联动,但是了直辖市只有两级,所以我们人工的给它加上一级。以北京市为例:变成了 北京 -> 北京市- >东城区,对于其他的直辖市也是同样的处理逻辑。

  修正好的数据奉上,有需要的小伙伴可以自取哦。

  • JSON-2020-11县以上行政区划代码
  • SQL-2020-11县以上行政区划代码

总结

  总体来讲,这个爬虫比较简单,只有简单的几行代码。毕竟网站也没啥反扒的机制,所以很轻松的就拿到了数据。

结尾

  嘿嘿话说,你都爬过哪些网站呢?

  如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。

  我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!

以上是 【Java】实战|省市区三级联动数据爬取 的全部内容, 来源链接: utcz.com/a/100494.html

回到顶部