前言:
最近開始學習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();
}
}
控制檯打印信息如下(只截圖了部分):
生成的文件如下(只截圖了部分):