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

时间:2022-06-11 00:32:45

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

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

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

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

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

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

第二步、数据源分析:

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

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

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

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

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

通过分析首页源码我们可以得到如下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。其他均一致。这样我们就可以用一个通用的方法解决这三个页面的数据爬取。  

  

  接下来我们分析一下数据源的结构,先从首页说起:  Jsoup获取全国地区数据属性值(省市县镇村)

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

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

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

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

第三步、编码实现:

?
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
 * 全国省市县镇村数据爬取
 * @author liushaofeng
 * @date -- 上午::
 * @version ..
 */
public class JsoupTest
{
  private static Map<Integer, String> cssMap = new HashMap<Integer, String>();
  private static BufferedWriter bufferedWriter = null;
  static
  {
    cssMap.put(, "provincetr");// 省
    cssMap.put(, "citytr");// 市
    cssMap.put(, "countytr");// 县
    cssMap.put(, "towntr");// 镇
    cssMap.put(, "villagetr");// 村
  }
  public static void main(String[] args) throws IOException
  {
    int level = ;
    initFile();
    // 获取全国各个省级信息
    Document connect = connect("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm//");
    Elements rowProvince = connect.select("tr." + cssMap.get(level));
    for (Element provinceElement : rowProvince)// 遍历每一行的省份城市
    {
      Elements select = provinceElement.select("a");
      for (Element province : select)// 每一个省份(四川省)
      {
        parseNextLevel(province, level + );
      }
    }
    closeStream();
  }
  private static void initFile()
  {
    try
    {
      bufferedWriter = new BufferedWriter(new FileWriter(new File("d:\\CityInfo.txt"), true));
    } catch (IOException e)
    {
      e.printStackTrace();
    }
  }
  private static void closeStream()
  {
    if (bufferedWriter != null)
    {
      try
      {
        bufferedWriter.close();
      } catch (IOException e)
      {
        e.printStackTrace();
      }
      bufferedWriter = null;
    }
  }
  private static void parseNextLevel(Element parentElement, int level) throws IOException
  {
    try
    {
      Thread.sleep();//睡眠一下,否则可能出现各种错误状态码
    } catch (InterruptedException e)
    {
      e.printStackTrace();
    }
    Document doc = connect(parentElement.attr("abs:href"));
    if (doc != null)
    {
      Elements newsHeadlines = doc.select("tr." + cssMap.get(level));//
      // 获取表格的一行数据
      for (Element element : newsHeadlines)
      {
        printInfo(element, level + );
        Elements select = element.select("a");// 在递归调用的时候,这里是判断是否是村一级的数据,村一级的数据没有a标签
        if (select.size() != )
        {
          parseNextLevel(select.last(), level + );
        }
      }
    }
  }
  /**
  * 写一行数据到数据文件中去
  * @param element 爬取到的数据元素
  * @param level 城市级别
  */
  private static void printInfo(Element element, int level)
  {
    try
    {
      bufferedWriter.write(element.select("td").last().text() + "{" + level + "}["
        + element.select("td").first().text() + "]");
      bufferedWriter.newLine();
      bufferedWriter.flush();
    } catch (IOException e)
    {
      e.printStackTrace();
    }
  }
  private static Document connect(String url)
  {
    if (url == null || url.isEmpty())
    {
      throw new IllegalArgumentException("The input url('" + url + "') is invalid!");
    }
    try
    {
      return Jsoup.connect(url).timeout( * ).get();
    } catch (IOException e)
    {
      e.printStackTrace();
      return null;
    }
  }
}

数据爬取过程便是一个漫长的过程,只需要慢慢等待吧,呵呵,由于程序运行时间较长,请不要在控制台打印输出,否则可能会影响程序运行....

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

市辖区{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]

......

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