整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取

本文作者:程序员内点事

更多精选

写在前边

受疫情影响一直在家远程办公,公司业务进展的缓慢,老实讲活并没有那么多,每天吃饭、睡觉、逛技术社区、写博客,摸鱼摸得爽的很。早上本来还想在来个回笼觉,突然部门经理的语音消息就过来了,甩给我一个连接地址 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/,要我把全国的省市名称和区域代码弄出来,建一个字典表,时限一上午。

分下一下需求

要全国的省、市名称,建一张字典表进行存储,表结构设计相对容易,那么城市数据该怎么搞?

有两种解决办法:

  1. 辛苦点复制粘贴,说多了也就几百个而已

  2. 写个爬虫工具,一劳永逸

但作为一个程序员没有什么是不能用程序解决的,虽然工作Ctrl+CCtrl+V用的不少,像这种没有技术含量的复制粘贴还是挺丢面子的。

爬虫搞起

基于这个需求只想要城市名称,爬虫工具选的是Jsoup,Jsoup是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

Jsoup是根据HTML页面的<body><td><tr>等标签来获取文本内容的,所以先分析一下目标页面结构。打开F12查看页面结构发现,我们要的目标数据在第5个<tbody>标签 class 属性为provincetr<tr> 标签里。



省份名称内容的页面结构如下:

  1. <tr class="provincetr">
  2. <td>
  3. <a href="11.html">北京市<br></a>
  4. </td>
  5. <td>
  6. <a href="12.html">天津市<br>
  7. </td>
  8. .........
  9. </tr>

再拿到<td>标签中<a>标签属性就可以了,省份名称找到了,再看看省对应的城市名在哪里,属性href="11.html" 就是省份下对应的城市页面Url http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/11.html

找城市名称跟上边分析过程一样,这里就不再赘述了,既然数据位置已经确定了,那么就开始具体的爬取工作了。

爬虫实现

1、引入Jsoup依赖
  1. <dependency>
  2. <groupId>org.jsoup</groupId>
  3. <artifactId>jsoup</artifactId>
  4. <version>1.7.3</version>
  5. </dependency>
2、代码编写

代码的实现比较简单就两个方法而已,没有什么难度主要是得细心,捋清页面标签的嵌套结构就可以了。按照之前分析的逻辑一步一步走

  1. /**
  2. * @author xin
  3. * @description 解析省份
  4. * @date 2019/11/4 19:24
  5. */
  6. public static void parseProvinceName(Map<String, Map<String, String>> map, String url) throws IOException {
  7. /**
  8. * 获取页面文档数据
  9. */
  10. Document doc = Jsoup.connect(url).get();
  11. /**
  12. * 获取页面上所有的tbody标签
  13. */
  14. Elements elements = doc.getElementsByTag("tbody");
  15. /**
  16. * 拿到第五个tbody标签
  17. */
  18. Element element = elements.get(4);
  19. /**
  20. * 拿到tbody标签下所有的子标签
  21. */
  22. Elements childrens = element.children();
  23. /**
  24. * 当前页面的URL
  25. */
  26. String baseUri = element.baseUri();
  27. for (Element element1 : childrens) {
  28. Elements provincetrs = element1.getElementsByClass("provincetr");
  29. for (Element provincetr : provincetrs) {
  30. Elements tds = provincetr.getElementsByTag("td");
  31. for (Element td : tds) {
  32. String provinceName = td.getElementsByTag("a").text();
  33. String href = td.getElementsByTag("a").attr("href");
  34. System.out.println(provinceName + " " + baseUri + "/" + href);
  35. map.put(provinceName, null);
  36. /**
  37. * 组装城市页面的URL,进入城市页面爬城市名称
  38. */
  39. parseCityName(map, baseUri + "/" + href, provinceName);
  40. }
  41. }
  42. }
  43. }

在抓取城市名称的时候有一点要注意,直辖市城市的省份和城市名称是一样的

  1. /**
  2. * @author xin
  3. * @description 解析城市名称
  4. * @date 2019/11/4 19:26
  5. */
  6. public static void parseCityName(Map<String, Map<String, String>> map, String url, String provinceName) throws IOException {
  7. Document doc = Jsoup.connect(url).get();
  8. Elements elements = doc.getElementsByTag("tbody");
  9. Element element = elements.get(4);
  10. Elements childrens = element.children();
  11. /**
  12. *
  13. */
  14. String baseUri = element.baseUri();
  15. Map<String, String> cityMap = new HashMap<>();
  16. for (Element element1 : childrens) {
  17. Elements citytrs = element1.getElementsByClass("citytr");
  18. for (Element cityTag : citytrs) {
  19. Elements tds = cityTag.getElementsByTag("td");
  20. /**
  21. * 直辖市,城市名就是本身
  22. */
  23. String cityName = tds.get(1).getElementsByTag("a").text();
  24. if (cityName.equals("市辖区")) {
  25. cityName = provinceName;
  26. }
  27. String href1 = tds.get(1).getElementsByTag("a").attr("href");
  28. System.out.println(cityName + " " + href1);
  29. cityMap.put(cityName, href1);
  30. }
  31. }
  32. map.put(provinceName, cityMap);
  33. }
  1. public class test2 {
  2. public static void main(String[] args) throws IOException {
  3. Map<String, Map<String, String>> map = new HashMap<>();
  4. parseProvinceName(map, "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018");
  5. System.out.println(JSON.toJSONString(map));
  6. }
  7. }
3、输出JSON字符串

当前只要省份和城市名称,爬虫没有什么深度,如果还需要区县等信息,可以根据市后边的url 35/3508.html 继续往下爬取

  1. {
  2. "福建省": {
  3. "龙岩市": "35/3508.html",
  4. "南平市": "35/3507.html",
  5. "莆田市": "35/3503.html",
  6. "福州市": "35/3501.html",
  7. "泉州市": "35/3505.html",
  8. "漳州市": "35/3506.html",
  9. "厦门市": "35/3502.html",
  10. "三明市": "35/3504.html",
  11. "宁德市": "35/3509.html"
  12. },
  13. "西藏自治区": {
  14. "拉萨市": "54/5401.html",
  15. "昌都市": "54/5403.html",
  16. "日喀则市": "54/5402.html",
  17. "那曲市": "54/5406.html",
  18. "林芝市": "54/5404.html",
  19. "山南市": "54/5405.html",
  20. "阿里地区": "54/5425.html"
  21. },
  22. "贵州省": {
  23. "贵阳市": "52/5201.html",
  24. "毕节市": "52/5205.html",
  25. "铜仁市": "52/5206.html",
  26. "六盘水市": "52/5202.html",
  27. "遵义市": "52/5203.html",
  28. "黔西南布依族苗族自治州": "52/5223.html",
  29. "安顺市": "52/5204.html",
  30. "黔东南苗族侗族自治州": "52/5226.html",
  31. "黔南布依族苗族自治州": "52/5227.html"
  32. },
  33. "上海市": {
  34. "上海市": "31/3101.html"
  35. },
  36. "湖北省": {
  37. "黄冈市": "42/4211.html",
  38. "孝感市": "42/4209.html",
  39. "恩施土家族苗族自治州": "42/4228.html",
  40. "省直辖县级行政区划": "42/4290.html",
  41. "襄阳市": "42/4206.html",
  42. "鄂州市": "42/4207.html",
  43. "十堰市": "42/4203.html",
  44. "咸宁市": "42/4212.html",
  45. "黄石市": "42/4202.html",
  46. "荆州市": "42/4210.html",
  47. "随州市": "42/4213.html",
  48. "宜昌市": "42/4205.html",
  49. "武汉市": "42/4201.html",
  50. "荆门市": "42/4208.html"
  51. },
  52. "湖南省": {
  53. "湘潭市": "43/4303.html",
  54. "衡阳市": "43/4304.html",
  55. "张家界市": "43/4308.html",
  56. "益阳市": "43/4309.html",
  57. "岳阳市": "43/4306.html",
  58. "娄底市": "43/4313.html",
  59. "株洲市": "43/4302.html",
  60. "常德市": "43/4307.html",
  61. "湘西土家族苗族自治州": "43/4331.html",
  62. "郴州市": "43/4310.html",
  63. "邵阳市": "43/4305.html",
  64. "长沙市": "43/4301.html",
  65. "永州市": "43/4311.html",
  66. "怀化市": "43/4312.html"
  67. },
  68. "广东省": {
  69. "河源市": "44/4416.html",
  70. "韶关市": "44/4402.html",
  71. "茂名市": "44/4409.html",
  72. "汕头市": "44/4405.html",
  73. "清远市": "44/4418.html",
  74. "深圳市": "44/4403.html",
  75. "珠海市": "44/4404.html",
  76. "广州市": "44/4401.html",
  77. "肇庆市": "44/4412.html",
  78. "中山市": "44/4420.html",
  79. "江门市": "44/4407.html",
  80. "云浮市": "44/4453.html",
  81. "惠州市": "44/4413.html",
  82. "湛江市": "44/4408.html",
  83. "东莞市": "44/4419.html",
  84. "揭阳市": "44/4452.html",
  85. "阳江市": "44/4417.html",
  86. "佛山市": "44/4406.html",
  87. "汕尾市": "44/4415.html",
  88. "潮州市": "44/4451.html",
  89. "梅州市": "44/4414.html"
  90. }
  91. .......
  92. }

Jsoup爬取页面数据对网络的依赖比较高,像我500块一年50M的方正宽带快要卡出翔了,平均执行三次才有一次成功,我太难了!

  1. Exception in thread "main" java.net.SocketTimeoutException: Read timed out
  2. at java.net.SocketInputStream.socketRead0(Native Method)
  3. at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
  4. at java.net.SocketInputStream.read(SocketInputStream.java:171)
  5. at java.net.SocketInputStream.read(SocketInputStream.java:141)
  6. at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
  7. at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
  8. at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
  9. at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735)
  10. at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678)
  11. at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
  12. at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
  13. at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
  14. at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:443)
  15. at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:465)
  16. at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:424)
  17. at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:178)
  18. at org.jsoup.helper.HttpConnection.get(HttpConnection.java:167)
  19. at com.xinzf.project.jsoup.test2.parseProvinceName(test2.java:32)
  20. at com.xinzf.project.jsoup.test2.main(test2.java:17)

总结

从分析页面到编写代码花费的时间,可能要比简单的复制粘贴还要长,但我依然选择用程序解决问题,并不是因为我有多勤快,反而是因为我很懒,你品,你细品!


今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞

为了不复制粘贴,我被逼着学会了JAVA爬虫的更多相关文章

  1. [转]Vim 复制粘贴探秘

    Vim作为最好用的文本编辑器之一,使用vim来编文档,写代码实在是很惬意的事情.每当学会了vim的一个新功能,就会很大地提高工作效率.有人使用vim几十年,还没有完全掌握vim的功能,这也说明了vim ...

  2. Vim 复制粘贴探秘

    Vim作为最好用的文本编辑器之一,使用vim来编文档,写代码实在是很惬意的事情.每当学会了vim的一个新功能,就会很大地提高工作效率.有人使用vim几十年,还没有完全掌握vim的功能,这也说明了vim ...

  3. Android中的复制粘贴

    Android中的复制粘贴 The Clipboard Framework 当使用clipboard framework时,把数据放在一个剪切对象(clip object)里,然后这个对象会放在系统的 ...

  4. 远程桌面时plsql的复制粘贴功能失效

    解决办法:重新启动远程桌面上的rdpclip进程就可以复制粘贴了,但是每次重开远程桌面都会出现同样的问题.可以rdpclip这个设置成开机启动.

  5. 不注册COM在Richedit中使OLE支持复制粘贴

    正常情况下在Richedit中使用OLE,如果需要OLE支持复制粘贴,那么这个OLE对象必须是已经注册的COM对象. 注册COM很简单,关键问题在于注册时需要管理员权限,这样一来,如果希望APP做成绿 ...

  6. ios textfield / textview长按复制粘贴中文显示

    当我们在写应用时要复制粘贴文本框内容时,默认显示的文字为英文字体,可按如下步骤设置,显示中文:

  7. 复制粘贴出来的悲剧----spring实现文件下载和HttpStatus.CREATED

    今天真是被自己的懒惰和复制粘贴给坑惨了... 网上有这么一个spring下载文件的最佳实践: @RequestMapping("download") public Response ...

  8. ZeroClipboard跨浏览器复制粘贴

    <!DOCTYPE html> <html> <head> <title>ZeroClipboard跨浏览器复制粘贴</title> < ...

  9. shutil复制粘贴和压缩

    shutil复制粘贴和压缩 shutil 高级的文件.文件夹.压缩包处理模块 @1).将文件内容拷贝到另一个文件中 import shutil shutil.copyfileobj(open(&quo ...

随机推荐

  1. jenkins+ant+jmeter自动化环境搭建

    jmeter:测试接口的工具,支持java语言: ant:Apache Ant是一个Java库和命令行工具,其任务是将构建文件中描述的进程作为相互依赖的目标和扩展点.只要使用过Linux系统的读者,应 ...

  2. spring之为什么要使用AOP(面向切片编程)?

    需求1-日志:在程序执行期间追踪正在发生的活动: 需求2-验证:希望计算器只处理正数的运算: 一.普通方法实现 Calculator.java package com.gong.spring.aop. ...

  3. .NET C# 红包生成算法,可设置红包总额和数量,可限制最大最小红包

    很多场景算红包的要求:根本问题就是指定的钱,指定的个数,红包发完,钱不剩余,最小红包1分钱,最大也需要限制. 原理:割绳子算法:每次都取最大值为总绳长的随机值,最后将其排序,计算每两个的差值,总差值即 ...

  4. 结合docker发布后端项目(基于gradle包管理)的shell脚本

    结合docker发布后端项目(基于gradle包管理)的shell脚本 本教程依据个人理解并经过实际验证为正确,特此记录下来,权当笔记. 注:基于linux操作系统(敏感信息都进行了处理) 目前主流的 ...

  5. Spring Cloud Stream消息驱动@SendTo和消息降级

    参考程序员DD大佬的文章,自己新建demo学习学习,由于需要消息回执,看到了@SendTo这个注解能够实现,下面开始学习demo,新建两个项目cloud-stream-consumer消费端 和 cl ...

  6. mysql 执行计划查看

    使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈.explain执行计划包含的信息 其中最重要的字段为:id ...

  7. 6年iOS开发被裁员,是行业的饱和还是经验根本不值钱?

    前言: 最近看到很多iOS开发由于公司裁员而需要重新求职的.他们普遍具有4年甚至更长的工作经验.但求职结果往往都不太理想. 我在与部分iOS开发者交谈的过程中发现,很多人的工作思路不清晰,技能不扎实, ...

  8. Class 'org.apache.tomcat.jdbc.pool.DataSource' not found

    把项目移动到新的运行环境时,明明包都导入了,项目也放进tomcat里面了,但是还会找不到该类 解决方法:项目右键选择底下的Properties ->project facets ->jav ...

  9. css部分概念

    1.层叠 规则之间属性相同,值不同的时候就会发生声明冲突,这个时候层叠就会起作用了,层叠会将我们声明的不同的值进行保留,相同的值进行比较,选权重值更高的一个来运行.具体情境如下:假设我们定义了一个di ...

  10. TieredMergePolicy

    setFloorSegmentMB多少MB一个层级,在此区间的segment分为一个floor. setMaxMergeAtOnce一次merge多少个segment. setSegmentsPerT ...