【前言】
你是否也曾羨慕過有些 phython 大神有著如下的神操作:

他們就輕輕的執行一串代碼,就能循環的抓取很多自己想要的數據。
其實不用太羨慕他們,因為不光 phython 能實現,我們用 JAVA 同樣也能夠輕松實現。
閑話不多說,下面我們直接開始實戰:
【1】創建項目
(1.1)我們用 IDEA(Eclipse同理) 創建一個全新的maven工程,我這里取名工程名 zyqok,各位隨意。

(1.2)在 pom.xml 里面加上 <dependencies>

(1.3)創建 Test 類,好了工程就已經搭好了。

【2】Httpclient 實現網絡請求
(2.1)什么是 httpclient ?
Httpclient 是 Apache 的一個子項目,它是一個為 Java 可以實現網絡請求的客戶端工具包。
簡單的說,他是一個 Jar 包,有了他,我們通過 Java 程序就可以實現網絡請求。
(2.2) 復制下面的 httpclient 依賴,加入到 pom.xml 文件中。
<!-- httpclient 核心包 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>

(2.3)創建一個 HttpTool 的類,這個類我們專門用來實現網絡請求相關方法。

(2.4) 為了避免其他網站侵權問題,下面以我個人網站一個頁面為例(http://www.zyqok.cn/material/index),我們來抓取這個頁面上的所有圖片。

(2.5) 可以看得出,這是一個 get 請求,并且返回的是一個 html 頁面。所以我們在 HttpTool 類中加入一個如下方法體:
/**
* 實現Get請求
* @param url 請求地址
* @return 頁面內容
*/
public static String doGet(String url) {
return null;
}

(2.6)復制代碼,添加 get 實現方法:
// 構建get請求
HttpGet get = new HttpGet(url);
// 創建客戶端
CloseableHttpClient client = HttpClients.createDefault();
try {
// 客戶端執行請求,獲取響應
HttpResponse response = client.execute(get);
// 獲取響應的頁面內容
InputStream in = response.getEntity().getContent();
StringBuilder sb = new StringBuilder();
byte[]b = new byte[102400];
int length;
while ((length = in.read(b)) != -1) {
sb.Append(new String(b, 0, length, "utf-8"));
}
// 返回頁面內容
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}

(2.7)OK,網絡請求相關實現類我們已經寫好了,我們接下來測試下,我們在 Test 類的 main 方法里加入如下代碼:
String html = HttpTool.doGet("http://www.zyqok.cn/material/index");
System.out.println(html);

(2.8)執行程序,查看結果。可以看到我們確實已經通過請求,獲取到網頁的返回內容了。

【3】Jsoup 解析網頁
在整個【2】的實現過程中,我們已經拿到網頁返回的數據,但我們要的是整個網頁中的圖片,并不是這種雜亂無章的網頁頁面數據,那么我們該怎么辦呢?簡單,接下來我們需要用到另外一種技術了 ---- Jsoup。
(3.1)什么是 Jsoup 技術?
下面是度娘給出的一個官方解釋:Jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,css以及類似于jQuery的操作方法來取出和操作數據(摘自百度)。
下面再用我個人語言簡單的總結下:Jsoup 技術就是用來處理各種 html 頁面 和 xml 數據。我們這里可以通過 Jsoup 來處理【2】中返回的 html 頁面。
(3.2)加入 Jsoup 依賴
我們在 pom.xml 加入如下依賴:
<!-- Jsoup 核心包 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>

(3.3)當然,使用 Jsoup 之前,我們需要對響應的 HTML 頁面進行分析,分析主要作用是:如何定位篩選出我們需要的數據?
我們把【2】中獲取到的頁面響應拷貝到 txt 文本中,然后可以發現:每個圖片它都包含在一個 div 中,且該div 有一個名為 material-div 的 class。

(3.4)按照上面分析:首先我們要獲取到包含圖片的所有 div,于是我們修改main方法中代碼為如下:
String html = HttpTool.doGet("http://www.zyqok.cn/material/index");
// 將 html 頁面解析為 Document 對象
Document doc = Jsoup.parse(html);
// 獲取所有包含 class = material-div 的 div 元素
Elements elements = doc.select("div.material-div");
for(Element div: elements){
System.out.println(div.toString());
}
注意:doc.select() 括號中的參數為過濾條件,基本等同于 Jquery 的過濾條件,所以會Jquery的同學,如何篩選條件基本就得心應手的,當然不會寫篩選條件的也不要怕,這里有一份 Jsoup 使用指南,閣下不妨收下(傳送門:Jsoup 官方使用指南)。

(3.5)我們執行代碼,將輸出結果繼續拷貝到文本中。
可以看到,本次確實只有圖片相關的div元素了,但這并不是我們想要的最終結果,我們最終的結果是獲取到所有圖片。
所以我們還需要繼續分析:如何獲取所有圖片的鏈接和名字。

(3.6)由于每個圖片所在的div元素結構都一樣,所以我們可以取隨機取一個div元素進行分析,于是我們可以取第一個div來進行分析,結構如下:
<div align="center" style="padding: 10px;" class="material-div">
<div style="width: 80px; height: 80px; margin-bottom: 3px; display: flex; align-items: center; justify-content: center">
<img class="fangda image" src="https://zyqok.oss-cn-chengdu.aliyuncs.com/20200414220946131_大樹夕陽.jpg">
<input type="hidden" class="materialId" value="121">
</div>
<font style="font-size: 5px">大樹夕陽.jpg</font><br>
<font style="font-size: 5px">2020-04-14 22:09:46</font>
</div>
3.7)我們可以看到,整個結構內,就一個 img 元素標簽,于是我們可以取第1個img標簽的 src 屬性為圖片鏈接;同理,我們取第1個 font 元素的文本內容為圖片名稱。

(3.8)于是我們可以修改循環中的代碼內容如下:
// 獲取第1個 img 元素Element img = div.selectFirst("img");// 獲取第1個 font 元素Element font = div.selectFirst("font");// 獲取img元素src屬性,即為圖片鏈接String url = img.attr("src");// 獲取name元素文本,即為圖片名稱String name = font.text();System.out.println(name + ": " + url);

(3.9)我們執行上面代碼,可以得出如下結果。

可以看到,這個頁面上的所有圖片地址和名稱已經被我們成功抓下來了。
【4】獲取圖片到本地
在第【3】步中,我們獲取到的只是所有圖片的鏈接,并沒有將所有圖片下載到我們本地,那么接下來,我們要將這個圖片下載到我們本地才算完成。
(4.1)既然要下載到本地,我們首先在本地找個地方,用于存放這些圖片。
比如:我將這圖片全部下載到 D:imgs(D 盤的 imgs 文件夾)中。

(4.2)我們在 HttpTool 類中增加保存圖片到本地的方法,代碼如下:
/**
* 保存圖片到本地
* @param src 圖片地址
* @param name 圖片名稱
*/
public static void saveImg(String src, String name) {
// 構建get請求
HttpGet get = new HttpGet(src);
// 創建客戶端
CloseableHttpClient client = HttpClients.createDefault();
try {
// 客戶端執行請求,獲取響應
HttpResponse response = client.execute(get);
// 獲取響應的頁面內容
InputStream in = response.getEntity().getContent();
int length;
byte[] bytes = new byte[1024];
FileOutputStream fos = new FileOutputStream("D:\imgs\" + name);
while ((length = in.read(bytes)) != -1) {
fos.write(bytes, 0, length);
fos.flush();
}
in.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}

(4.3)修改 Test 類 main 方法最終代碼如下:
public static void main(String args[]) throws Exception {
String html = HttpTool.doGet("http://www.zyqok.cn/material/index");
// 將 html 頁面解析為 Document 對象
Document doc = Jsoup.parse(html);
// 獲取所有包含 class = material-div 的 div 元素
Elements elements = doc.select("div.material-div");
for (int i = 0; i<elements.size(); i++) {
Element div = elements.get(i);
// 獲取第1個 img 元素
Element img = div.selectFirst("img");
// 獲取第1個 font 元素
Element font = div.selectFirst("font");
// 獲取img元素src屬性,即為圖片鏈接
String src = img.attr("src");
// 獲取name元素文本,即為圖片名稱
String name = font.text();
if (!name.contains(".")) {
name += ".jpg";
}
HttpTool.saveImg(src, i + name);
System.out.println("抓取第 " + i + " 張圖片成功! 圖片名稱 : " + name);
}
System.out.println("所有圖片抓取完成 !!");
}

(4.4)執行代碼,打印如下圖,看到這個結果,是不是感覺有點文章開頭的展示味道了。
最后,我們只需要去本地文件夾下看看,所有圖片是否成功保存到了本地?如果有圖片,則我們就成功了。

(4.5)我們打開D盤imgs文件夾,可以看到網站上的圖片確實已經全部保存到本地了。

【5】結尾語
通過我們 [批量抓取網絡圖片] 這一實戰案例,我們可以感受到:通過 Httopclient 和 Jsoup 這兩種技術,不僅僅可以批量抓取數據,其實還可以實現很多功能。
比如:網站登錄,分布式服務器之間的數據傳遞,三方平臺的API對接,有效數據的篩選和保存,數據的二次加工等等。