Jsoup获取全国地区数据(省市县镇村)

最近手头在做一些东西,需要一个全国各地的地域数据,从省市区到县镇乡街道的。各种度娘,各种谷歌,都没找到一个完整的数据。最后功夫不负有心人,总算找到一份相对来说比较完整的数据,但是这里的数据也只是精确到镇级别,没有村一级的数据(后来通过分析数据源我知道了为什么,呵呵),在加上博主提供的有些数据存在冗余,对于有强迫症和追求完美的我,心想着我一定要自己动手去把这部分数据给爬取出来。

  上述博文中的内容还算丰富,博主是用的是php来实现的,作为2015年度编程语言排行榜的第一位,我们也不能示弱啊,下面我就带着大家一起来看看用java怎么从网页当中爬取我们想要的数据...

第一步、准备工作(数据源+工具):

  数据源(截止目前最全面权威的官方数据):http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/

  爬取数据的工具(爬虫工具):http://jsoup.org/

第二、数据源分析:

  首先jsoup工具的使用我在这里就不做讲解了,感兴趣的可以自己动手去查阅。

  做开发就应该多去了解一些软件工具的使用,在平常开发过程中遇到了才知道从何下手,鼓励大家多平时留意一些身边的软件工具,以备不时之需。在做 这个东西以前,我也不知道jsoup要怎么用,但我知道jsoup可以用来干嘛,在我需要的用到的时候,再去查阅资料,自己学习。

 

  上述的数据源是2013年中华人民共和国国家统计局发布的,其准确性和权威性不言而喻。

  接下来我们分析一下数据源的结构,先从首页说起:   

  通过分析首页源码我们可以得到如下3点:

  1. 页面的整个布局是用的table标签来控制的,也就是说我们如果要通过jsoup来选择超链接,那么一定要注意,上图中不是只要标注了省市地区的地方采用的才是表格,整个页面中存在多个表格,因此是不可以直接通过表格
    Document connect = connect("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/");
    Elements rowProvince = connect.select("table");

    来解析数据的。

  2. 页面中有超链接的部分有多少地方。可能是官方考虑到了你们这种程序员需要获取这样的数据的原因吧,页面很干净,除开下方的备案号是多余的超链接,其他的链接可以直接爬取。
  3. 省份城市的数据规律。包含有效信息的表格的每一行都有一个class属性provincetr,这个属性很重要,至于为什么重要,请接着往下看; 每一行数据中存在多个td标签,每一个td标签中包含一个a超链接,而这个超链接正是我们想要的超链接,超链接的文本即使省份(直辖市等)的名称。

   

  再次我们再看一下一般的数据页面(一般的数据页面包括市级、县级、镇级这三级数据展示页面):

  之所以要把上述三个页面放在一起,是因为通过分析我们可以发现,这三级数据的数据页面完全一致,唯一不同的就是在html源码数据表格中的数据 行tr的class属性不一致,分别对应为:citytr,countrytrhe towntr。其他均一致。这样我们就可以用一个通用的方法解决这三个页面的数据爬取。  

  

  最后看看村一级的数据页面:   

   在村一级的数据中,和上述市县镇的数据格式不一致,这一级所表示的数据是最低一级的,所以不存在a链接,因此不能采用上面市县镇数据的爬取方 式去爬取;这里展示数据的表格行的class为villagetr,除开这两点以外,在每一行数据中包含三列数据,第一列是citycode,第二列是城 乡分类(市县镇的数据格式不存在这一项),第三列是城市名称。

   把握了以上各个要点之外,我们就可以开始编码了。

第三步、编码实现: 
复制代码
  1 import java.io.BufferedWriter;
  2 import java.io.File;  3 import java.io.FileWriter;  4 import java.io.IOException;  5 import java.util.HashMap;  6 import java.util.Map;  7  8 import org.jsoup.Jsoup;  9 import org.jsoup.nodes.Document;  10 import org.jsoup.nodes.Element;  11 import org.jsoup.select.Elements;  12  13 /**  14  * 全国省市县镇村数据爬取  15  * @author liushaofeng  16  * @date 2015-10-11 上午12:19:39  17  * @version 1.0.0  18 */  19 public class JsoupTest  20 {  21 private static Map<Integer, String> cssMap = new HashMap<Integer, String>();  22 private static BufferedWriter bufferedWriter = null;  23  24 static  25  {  26 cssMap.put(1, "provincetr");// 27 cssMap.put(2, "citytr");// 28 cssMap.put(3, "countytr");// 29 cssMap.put(4, "towntr");// 30 cssMap.put(5, "villagetr");// 31  }  32  33 public static void main(String[] args) throws IOException  34  {  35 int level = 1;  36  37  initFile();  38  39 // 获取全国各个省级信息  40 Document connect = connect("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/");  41 Elements rowProvince = connect.select("tr." + cssMap.get(level));  42 for (Element provinceElement : rowProvince)// 遍历每一行的省份城市  43  {  44 Elements select = provinceElement.select("a");  45 for (Element province : select)// 每一个省份(四川省)  46  {  47 parseNextLevel(province, level + 1);  48  }  49  }  50  51  closeStream();  52  }  53  54 private static void initFile()  55  {  56 try  57  {  58 bufferedWriter = new BufferedWriter(new FileWriter(new File("d:\\CityInfo.txt"), true));  59 } catch (IOException e)  60  {  61  e.printStackTrace();  62  }  63  }  64  65 private static void closeStream()  66  {  67 if (bufferedWriter != null)  68  {  69 try  70  {  71  bufferedWriter.close();  72 } catch (IOException e)  73  {  74  e.printStackTrace();  75  }  76 bufferedWriter = null;  77  }  78 } 79 80 private static void parseNextLevel(Element parentElement, int level) throws IOException 81 { 82 try 83 { 84 Thread.sleep(500);//睡眠一下,否则可能出现各种错误状态码 85 } catch (InterruptedException e) 86 { 87 e.printStackTrace(); 88 } 89 90 Document doc = connect(parentElement.attr("abs:href")); 91 if (doc != null) 92 { 93 Elements newsHeadlines = doc.select("tr." + cssMap.get(level));// 94 // 获取表格的一行数据 95 for (Element element : newsHeadlines) 96 { 97 printInfo(element, level + 1); 98 Elements select = element.select("a");// 在递归调用的时候,这里是判断是否是村一级的数据,村一级的数据没有a标签 99 if (select.size() != 0) 100 { 101 parseNextLevel(select.last(), level + 1); 102 } 103 } 104 } 105 } 106 107 /** 108 * 写一行数据到数据文件中去 109 * @param element 爬取到的数据元素 110 * @param level 城市级别 111 */ 112 private static void printInfo(Element element, int level) 113 { 114 try 115 { 116 bufferedWriter.write(element.select("td").last().text() + "{" + level + "}[" 117 + element.select("td").first().text() + "]"); 118 bufferedWriter.newLine(); 119 bufferedWriter.flush(); 120 } catch (IOException e) 121 { 122 e.printStackTrace(); 123 } 124 } 125 126 private static Document connect(String url) 127 { 128 if (url == null || url.isEmpty()) 129 { 130 throw new IllegalArgumentException("The input url('" + url + "') is invalid!"); 131 } 132 try 133 { 134 return Jsoup.connect(url).timeout(100 * 1000).get(); 135 } catch (IOException e) 136 { 137 e.printStackTrace(); 138 return null; 139 } 140 } 141 }
复制代码
数据爬取过程便是一个漫长的过程,只需要慢慢等待吧,呵呵,由于程序运行时间较长,请不要在控制台打印输出,否则可能会影响程序运行....

最终获取到数据的格式如下("{}"中表示城市级别,"[]"中内容表示城市编码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
市辖区{ 3 }[ 110100000000 ]
东城区{ 4 }[ 110101000000 ]
东华门街道办事处{ 5 }[ 110101001000 ]
多福巷社区居委会{ 6 }[ 110101001001 ]
银闸社区居委会{ 6 }[ 110101001002 ]
东厂社区居委会{ 6 }[ 110101001005 ]
智德社区居委会{ 6 }[ 110101001006 ]
南池子社区居委会{ 6 }[ 110101001007 ]
黄图岗社区居委会{ 6 }[ 110101001008 ]
灯市口社区居委会{ 6 }[ 110101001009 ]
正义路社区居委会{ 6 }[ 110101001010 ]
甘雨社区居委会{ 6 }[ 110101001011 ]
台基厂社区居委会{ 6 }[ 110101001013 ]
韶九社区居委会{ 6 }[ 110101001014 ]
王府井社区居委会{ 6 }[ 110101001015 ]
景山街道办事处{ 5 }[ 110101002000 ]
隆福寺社区居委会{ 6 }[ 110101002001 ]
吉祥社区居委会{ 6 }[ 110101002002 ]
黄化门社区居委会{ 6 }[ 110101002003 ]
钟鼓社区居委会{ 6 }[ 110101002004 ]
魏家社区居委会{ 6 }[ 110101002005 ]
汪芝麻社区居委会{ 6 }[ 110101002006 ]
景山东街社区居委会{ 6 }[ 110101002008 ]
皇城根北街社区居委会{ 6 }[ 110101002009 ]
交道口街道办事处{ 5 }[ 110101003000 ]
交东社区居委会{ 6 }[ 110101003001 ]
福祥社区居委会{ 6 }[ 110101003002 ]
大兴社区居委会{ 6 }[ 110101003003 ]
府学社区居委会{ 6 }[ 110101003005 ]
鼓楼苑社区居委会{ 6 }[ 110101003007 ]
菊儿社区居委会{ 6 }[ 110101003008 ]
南锣鼓巷社区居委会{ 6 }[ 110101003009 ]
安定门街道办事处{ 5 }[ 110101004000 ]
交北头条社区居委会{ 6 }[ 110101004001 ]
北锣鼓巷社区居委会{ 6 }[ 110101004002 ]
国子监社区居委会{ 6 }[ 110101004003 ]
......

拿到以上数据以后,自己想干什么都可以自我去实现了,以上的代码可以直接运行,从数据源爬取后,可以直接转换成自己所要的格式。

转载于:https://www.cnblogs.com/likeju/p/4882890.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值