全国省市区java_Jsoup获取全国地区数据(省市县镇村)
最近正在着手进行一项项目,在收集全国范围内的人口与经济相关数据时遇到了不少困难。需要涵盖从省市区一直到县镇乡街道各个层级的数据信息。各大搜索引擎遍览无果后始终未能找到一份完整的数据资料。经过一番努力后发现终于寻得一份相对完整的数据资源但这些数据仅精确至镇级别并未包含村一级的信息(后来通过深入分析发现这是由于原始数据源的限制所致)。同时博主提供的某些数据存在冗余现象对于追求完美主义倾向的我而言实在无法忍受这类不完美之处后决定亲自尝试获取这部分地区的完整数据
上述博文中的内容还算丰富,在2015年份度的编程语言排行榜中位居榜首的博主同样采用了PHP技术进行开发。作为这一年的重量级编程语言之一,在竞争中占据主导地位的同时我们也不能掉以轻心接下来我会带领大家一起探索如何利用Java从网页中提取所需数据
第一步、准备工作(数据源+工具):
数据源(截至当前为止最详尽、具有高度权威性的官方统计数据):http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/
爬取数据的工具(爬虫工具):http://jsoup.org/
第二步、数据源分析:
在本节中我对jsoup工具的使用不做详细讲解
开发过程中应当熟悉掌握一些常用软件工具的应用,在遇到具体问题时才能找到合适的解决方案。建议大家多多关注身边的一些常用软件工具,并做好充分准备以防不测。在之前的工作中我对JSOup的具体使用还不太清楚,在需要用到的时候再去查相关资料并进行学习。
所引用的数据资料来自2013年中华人民共和国国家统计局发布的这些数据的准确性与可靠性无需置疑
接下来我们分析一下数据源的结构,先从首页说起:

通过分析首页源码我们可以得到如下3点:
该网页的整体布局主要依赖于table标签来进行管理。即若我们要使用jsoup选择超链接,则需特别注意,在上图中,并不仅仅是标注有省市地区的地方才会使用表格结构。因此无法直接基于表格外设超链接。
Create a document link by establishing a connection to the specified URL.
Create a document link by establishing a connection to the specified URL.
Create a document link by establishing a connection to the specified URL.
Elements rowProvince = connect.select("table");
来解析数据的。
页面上包含超链接的部分的数量是多少?可能是因为官方考虑到你们这种程序员需要获取这类数据的缘故吧。页面很干净,在下方的备案号之外没有多余的超链接是可以直接抓取的数据。
中国各省市的数据特征主要体现在一张记录了关键数据的表格中。这张表格中的每一行都具有一个tag属性provincialtr,这一设置对于后续分析具有重要意义;在表格的具体内容中,每个td单元格内均嵌入了一个超链结,这些超链结直接连接到省(直辖市等)名称的相关信息,其内容即为省(直辖市等)名称的具体体现
再来一次,我们进一步了解常见的一般数据页面(常见的一般数据页面包括这三个等级的数据展示页面):
由于对这三个页面进行分析后会发现,在这三层数据中,其数据页面完全一致。唯一的不同点在于,在html源码中的数据表格部分,行tr的class属性存在差异。具体来说,在html源码中的数据表格部分,行tr的class属性存在差异。在这一差异下分别对应于:citytr、countrytr、towntr。其余部分则保持一致。这样一来,我们便可以通过统一的方法来处理这三个页面的数据抓取问题。

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

在村一级的数据集中,在与上述市县镇的数据格式不一致的情况下,属于最低级别的数据,并且没有a链接的存在;因此无法采用上述 getCountysTown数据的爬取方式获取;展示的数据表格行具有villagetr类属性,在除这两点之外的情况下,则每一行包含三列信息:第一列为citycode, 第二列为城乡分类(其中 getCountysTown的数据格式中并未包含此字段),第三列为城市名称。
把握了以上各个要点之外,我们就可以开始编码了。
第三步、编码实现:

1 importjava.io.BufferedWriter;
2 importjava.io.File;
3 importjava.io.FileWriter;
4 importjava.io.IOException;
5 importjava.util.HashMap;
6 importjava.util.Map;
7
8 importorg.jsoup.Jsoup;
9 importorg.jsoup.nodes.Document;
10 importorg.jsoup.nodes.Element;
11 importorg.jsoup.select.Elements;
12
13 /**
14 * 全国省市县镇村数据爬取
15 * @authorliushaofeng
16 * @date 2015-10-11 上午12:19:39
17 * @version1.0.0
18 */
19 public classJsoupTest
20 {
21 private static Map cssMap = new HashMap();
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) throwsIOException
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 voidinitFile()
55 {
56 try
57 {
outputWriter = new bufferSize(new FileWriter(D:\ CityInfo.txt", true));
59 } catch(IOException e)
60 {
61 e.printStackTrace();
62 }
63 }
64
65 private static voidcloseStream()
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
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 selectedProperties = elem.select("href");// 在递归调用过程中, 判断当前层级是否为村庄级别的数据, 不包含标签
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 }

数据获取过程确实需要耗费不少时间,请耐心地静静期待完成的时刻吧。
长时间运行的程序若在控制台打印输出结果可能会干扰正常运行。
最终获取到数据的格式如下("{}"中表示城市级别,"[]"中内容表示城市编码):
获得上述数据后,根据个人需求完成各种任务;该代码可以直接执行;从数据源获取后,方便地转换为所需的数据形式。
