前言:
最近开始学习java爬虫,看过基础知识,准备找个例子试试手,于是就有下面这个东西,有参考其他的文章并结合自己的思想,可供跟我一样的java爬虫初学者参考。
目的:
通过网络爬虫爬取中国最小粒度的区域维度信息,包括省(province) 、市(city)、县(county)、镇(town)、村委会(village)。
主网站链接:
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2015/index.html
话不多说,直接上代码。
package com.basic.countrymessage;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName: Html
* @Author: guang
* @CreateDate: 2019/1/13 23:09
*/
public class Html {
//键存放url,值存放对应页面省、市、村类别,类似于{url,category}
private static Map<String, String> map = new HashMap<>();
//把所有的类别存放到一个数组里
private static String[] categorys = {"province","city","county","town","village"};
public static void main(String[] args) {
Html html = new Html();
//初始url
String indexUrl = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2015/index.html";
//将首页的url跟对应的类别存入map
map.put(indexUrl, categorys[0]);
//打印到控制台(递归)
html.printAll(indexUrl);
}
/**
* 根据url从网络获取网页文本
* @param url
* @return
*/
public Document getHtmlTtextByUrl(String url) {
Document doc = null;
try {
doc = Jsoup.connect(url).data("query", "java")
.userAgent("Mozilla").cookie("auth", "token")
.timeout(300000).post();
} catch (IOException e) {
e.printStackTrace();
try {
doc = Jsoup.connect(url).timeout(5000000).get();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return doc;
}
/**
* 根据本地路径获取网页文本,如果不存在就通过url从网络获取并保存
* @param name
* @param url
* @return
*/
public Document getHtmlTextByPath(String name, String url) {
String path = "D:/360downloads/wpcache/srvsetwp/" + name;
Document doc = null;
File file = new File(path);
try {
doc = Jsoup.parse(file, "GBK");
if (!doc.children().isEmpty()) {
System.out.println("已经存在。");
}
} catch (IOException e) {
System.out.println("文件未找到,正在从网页获取......");
doc = getHtmlTtextByUrl(url);
//并且保存到本地
this.SaveHtml(url, name);
}
return doc;
}
/**
* 将网页保存到本地
* @param url
* @param name
*/
public void SaveHtml(String url, String name) {
try {
File file = new File("D:/360downloads/wpcache/srvsetwp/" + name);
URL temp = new URL(url);
//字节输入流
InputStream in = temp.openStream();
//字节输出流
FileOutputStream fos = new FileOutputStream(file);
//为字节输入流增加缓冲
BufferedInputStream bis = new BufferedInputStream(in);
//为字节输出流增加缓冲
BufferedOutputStream bos = new BufferedOutputStream(fos);
int length;
byte[] buffer = new byte[1024*20];
while ((length = bis.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, length);
}
bos.close();
fos.close();
bis.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 根据元素属性获取某个元素内的elements列表
* @param doc
* @param className
* @return 返回所有的<tr></tr>集合
*/
public Elements getEleByClass(Document doc, String className) {
Elements ele = doc.select(className);
return ele;
}
/**
* 获取省、市、县的信息
* @param url
* @param type
* @return
*/
public ArrayList<String[]> getProvince(String url, String type) {
ArrayList<String[]> list = new ArrayList();
//"tr.provincetr","tr.citytr"
String classType = "." +type+ "tr";
//这里pathName是文件名,由于页面的url原因,我们获取的name后缀直接就有.html
String pathName = url.substring(url.lastIndexOf("/"));
Document doc2 = this.getHtmlTextByPath(pathName, url);
if (doc2 != null) {
//<tr></tr>的集合
Elements es = this.getEleByClass(doc2, classType);
for (Element e : es) {
if (e != null) {
//tr的子元素td,td内包含a标签
for (Element el : e.children()) {
//创建数组,保存名称跟对应的url
String[] strs = new String[2];
if (el.children().first() != null) {
strs[0] = el.children().first().ownText();
//因页面上href是相对路径,我们拼接一下获取绝对路径
strs[1] = url.substring(0, url.lastIndexOf("/")+1) + el.children().first().attr("href");
for (int i=0; i<categorys.length; i++) {
if (categorys[i].equals(type)) {
//因页面上我们要的a标签对应的type比我们实际要的大一级,所以这里我们把索引+1
map.put(strs[1], categorys[i+1]);
}
}
list.add(strs);
}
}
}
}
}
return list;
}
/**
* 打印信息
*/
public void printAll(String url) {
Html html = new Html();
ArrayList<String[]> list = html.getProvince(url, map.get(url));
for (String[] s : list) {
if (s[1] != null) {
System.out.printf(s[0]);
//递归继续打印
html.printAll(s[1]);
} else {
System.out.printf(s[0]);
}
}
System.out.println();
}
}
控制台打印信息如下(只截图了部分):
生成的文件如下(只截图了部分):