一、Jsoup简介
Jsoup是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
Jsoup是根据HTML页面的、、等标签来获取文本内容的,所以先分析一下目标页面结构。打开F12查看页面结构发现,我们要的目标数据在第5个标签 class 属性为provincetr 的 标签里。
图片
比如我们需要爬取的省份名称内容的页面结构如下:
<tr class="provincetr">
<td>
<a href="11.html">北京市<br></a>
</td>
<td>
<a href="12.html">天津市<br>
</td>
.........
</tr>
再拿到标签中标签属性就可以了,省份名称找到了,再看看省对应的城市名在哪里,属性href="11.html" 就是省份下对应的城市页面Url http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/11.html。
二、爬虫实现
1、引入Jsoup依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.3</version>
</dependency>
2、代码编写
代码的实现也比较简单,只需要按照原网站的元素结构解析嵌套结构就可以了,如下所示。
public static void parseProvinceName(Map<String, Map<String, String>> map, String url) throws IOException {
/**
* 获取页面文档数据
*/
Document doc = Jsoup.connect(url).get();
/**
* 获取页面上所有的tbody标签
*/
Elements elements = doc.getElementsByTag("tbody");
/**
* 拿到第五个tbody标签
*/
Element element = elements.get(4);
/**
* 拿到tbody标签下所有的子标签
*/
Elements childrens = element.children();
/**
* 当前页面的URL
*/
String baseUri = element.baseUri();
for (Element element1 : childrens) {
Elements provincetrs = element1.getElementsByClass("provincetr");
for (Element provincetr : provincetrs) {
Elements tds = provincetr.getElementsByTag("td");
for (Element td : tds) {
String provinceName = td.getElementsByTag("a").text();
String href = td.getElementsByTag("a").attr("href");
System.out.println(provinceName + " " + baseUri + "/" + href);
map.put(provinceName, null);
/**
* 组装城市页面的URL,进入城市页面爬城市名称
*/
parseCityName(map, baseUri + "/" + href, provinceName);
}
}
}
}
在抓取城市名称的时候有一点要注意,直辖市城市的省份和城市名称是一样的。
public static void parseCityName(Map<String, Map<String, String>> map, String url, String provinceName) throws IOException {
Document doc = Jsoup.connect(url).get();
Elements elements = doc.getElementsByTag("tbody");
Element element = elements.get(4);
Elements childrens = element.children();
/**
*
*/
String baseUri = element.baseUri();
Map<String, String> cityMap = new HashMap<>();
for (Element element1 : childrens) {
Elements citytrs = element1.getElementsByClass("citytr");
for (Element cityTag : citytrs) {
Elements tds = cityTag.getElementsByTag("td");
/**
* 直辖市,城市名就是本身
*/
String cityName = tds.get(1).getElementsByTag("a").text();
if (cityName.equals("市辖区")) {
cityName = provinceName;
}
String href1 = tds.get(1).getElementsByTag("a").attr("href");
System.out.println(cityName + " " + href1);
cityMap.put(cityName, href1);
}
}
map.put(provinceName, cityMap);
}
下面是一段测试代码。
public class test2 {
public static void main(String[] args) throws IOException {
Map<String, Map<String, String>> map = new HashMap<>();
parseProvinceName(map, "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018");
System.out.println(JSON.toJSONString(map));
}
}
需要说明的是,当前只要省份和城市名称,爬虫没有什么深度,如果还需要区县等信息,可以根据市后边的url 35/3508.html 继续往下爬取。