本文將手把手地教大家從零開始用Java寫一個簡單地爬蟲!
目標
爬取全景網圖片,並下載到本地
收穫
通過本文,你將複習到:
-
IDEA創建工程
-
IDEA導入jar包
-
爬蟲的基本原理
-
Jsoup的基本使用
-
File的基本使用
-
FileOutputStream的基本使用
-
ArrayList的基本使用
-
foreach的基本使用
說明
爬蟲所用的HTM解析器爲Jsoup。Jsoup可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據。
Jsoup相關API整理見文末附錄一。
開始
一、前端分析
1、使用Chrome或其他瀏覽器,打開全景網,按F12進入調試模式,分析網頁結構。(這裏選的是“創意”=>“優山美地”)
2、找規律,看圖片對應的結構是什麼。可以發現,每個圖片的結構都如下圖紅框所示。
3、找到結構後再找圖片鏈接。進一步分析後發現,圖片鏈接可以是下圖紅框部分。
4、複製到瀏覽器打開看看驗證下。(好吧,訪問這個URL直接給我下載了...)
5、前端部分分析完畢,接下來就可以用Java編碼了!
二、爬取思路
通過Java向全景網發送GET請求,以獲取HTML文件。Jsoup解析後尋找class=item lazy的a標籤,他的child節點(即<img>)就是我們要找的目標節點了,搜索到的應當是一個ArrayList。然後遍歷集合,從中搜尋圖片的URL,並下載到本地保存。(更深一步,可以下載完一頁後,繼續下載後一頁,直至全部下完。本文直講下載第一頁。提示一下,鏈接後面的topic/1其實就是當前頁數)
三、Java編碼
1、先下載Jsoup jar包,並導入到IDEA工程中。
2、新建Java工程。
3、簡單測試下get請求,若請求成功,則進入下一步;若報錯,檢查URL是否帶了中文。
package com.sxf;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
public class Main {
public static void main(String[] args) {
try {
Document doc = Jsoup.connect("https://www.quanjing.com/creative/topic/1").get();
System.out.println(doc);
}catch (Exception e){
e.printStackTrace();
}
}
}
4、尋找class爲item lazy的元素,找到他的child節點,返回ArrayList。並將圖片的URL單獨提取出來。
public static void main(String[] args) {
try {
Document doc = Jsoup.connect("https://www.quanjing.com/creative/topic/1").get();
// 尋找class爲item lazy的元素,返回ArrayList。
Elements items = doc.getElementsByClass("item lazy");
ArrayList<String> URLS = new ArrayList<>();
for (Element i : items) {
// 將圖片的URL單獨提取出來。
URLS.add(i.child(0).attr("src"));
}
for (String str : URLS) {
System.out.println(str);
}
}catch (Exception e){
e.printStackTrace();
}
}
5、我們先嚐試用Jsoup下載一張圖片試試效果。
// 獲取response
Connection.Response imgRes = Jsoup.connect(URLS.get(0)).ignoreContentType(true).execute();
FileOutputStream out = (new FileOutputStream(new java.io.File("demo.jpg")));
// imgRes.body() 就是圖片數據
out.write(imgRes.bodyAsBytes());
out.close();
可以看到在當前工程路徑下,生成了demo.jpg圖片,並且顯示正常!
6、接下來,我們要創建一個文件夾,用來專門存放圖片。
File相關API整理見文末附錄二。
//當前路徑下創建Pics文件夾
File file = new File("Pics");
file.mkdir();
System.out.println(file.getAbsolutePath());
7、接下來開始遍歷圖片並下載。由於圖片較多,爲了簡單起見,我們保存圖片時候的名稱,就從1開始依次增吧。
// 遍歷圖片並下載
int cnt = 1;
for (String str : URLS) {
System.out.println(">> 正在下載:"+str);
// 獲取response
Connection.Response imgRes = Jsoup.connect(str).ignoreContentType(true).execute();
FileOutputStream out = (new FileOutputStream(new java.io.File(file, cnt+".jpg")));
// imgRes.body() 就是圖片數據
out.write(imgRes.bodyAsBytes());
out.close();
cnt ++;
}
運行結果
到此編碼部分也結束了,完整代碼見文末附件三!
附錄一
Jsoup(HTML解析器)
繼承關係:Document繼承Element繼承Node。TextNode繼承Node。
->java.lang.Object
->org.jsoup.nodes.Node
->org.jsoup.nodes.Element
->org.jsoup.nodes.Document
html文檔:Document
元素操作:Element
節點操作:Node
官方API:https://jsoup.org/apidocs/org/jsoup/nodes/Document.html
一、解析HTML並取其內容
Document doc = Jsoup.parse(html);
二、解析一個body片斷
Document doc = Jsoup.parseBodyFragment(html);
Element body = doc.body();
三、從一個URL加載一個Document
Document doc = Jsoup.connect("http://example.com")
.data("query", "Java")
.userAgent("Mozilla")
.cookie("auth", "token")
.timeout(3000)
.post();
String title = doc.title();
四、從一個文件加載一個文檔
File input = new File("/tmp/input.html");
// baseUri 參數用於解決文件中URLs是相對路徑的問題。如果不需要可以傳入一個空的字符串
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
五、使用DOM方法來遍歷一個文檔
1、查找元素
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key) // 和相關方法
// 元素同級
siblingElements()
firstElementSibling()
lastElementSibling()
nextElementSibling()
previousElementSibling()
// 關係
parent()
children()
child(int index)
2、元素數據
// 獲取屬性attr(String key, String value)設置屬性
attr(String key)
// 獲取所有屬性
attributes()
id()
className()
classNames()
// 獲取文本內容text(String value) 設置文本內容
text()
// 獲取元素內HTMLhtml(String value)設置元素內的HTML內容
html()
// 獲取元素外HTML內容
outerHtml()
// 獲取數據內容(例如:script和style標籤)
data()
tag()
tagName()
3、操作HTML和文本
append(String html)
prepend(String html)
appendText(String text)
prependText(String text)
appendElement(String tagName)
prependElement(String tagName)
html(String value)
六、使用選擇器語法來查找元素(類似於CSS或jquery的選擇器語法)
//帶有href屬性的a元素
Elements links = doc.select("a[href]");
//擴展名爲.png的圖片
Elements pngs = doc.select("img[src$=.png]");
//class等於masthead的div標籤
Element masthead = doc.select("div.masthead").first();
//在h3元素之後的a元素
Elements resultLinks = doc.select("h3.r > a");
七、從元素抽取屬性、文本和HTML
1、要取得一個屬性的值,可以使用Node.attr(String key) 方法
2、對於一個元素中的文本,可以使用Element.text()方法
3、對於要取得元素或屬性中的HTML內容,可以使用Element.html(), 或 Node.outerHtml()方法
4、其他:
Element.id()
Element.tagName()
Element.className()
Element.hasClass(String className)
附錄二
File類
*java.io.File類用於表示文件或目錄。*
創建File對象:
// 文件/文件夾路徑對象
File file = new File("E:/...");
// 父目錄絕對路徑 + 子目錄名稱
File file = new File("..." ,"");
// 父目錄File對象 + 子目錄名稱
File file = new File("...","...");
file.exists():判斷文件/文件夾是否存在
file.delete():刪除文件/文件夾
file.isDirectory():判讀是否爲目錄
file.isFile():判讀是否爲文件夾
file.mkdir():創建文件夾(僅限一級目錄)
file.mkdirs():創建多及目錄文件夾(包括但不限一級目錄)
file.createNewFile():創建文件
file.getAbsolutePath():得到文件/文件夾的絕對路徑
file.getName():得到文件/文件夾的名字
file.String():同樣是得到文件/文件夾的絕對路徑等於file.getAbsolutePath()
file.getParent():得到父目錄的絕對路徑
附錄三
完整代碼
package com.sxf;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
try {
Document doc = Jsoup.connect("https://www.quanjing.com/creative/topic/1").get();
// 尋找class爲item lazy的元素,返回ArrayList。
Elements items = doc.getElementsByClass("item lazy");
ArrayList<String> URLS = new ArrayList<>();
// 將圖片的URL單獨提取出來。
for (Element i : items) {
URLS.add(i.child(0).attr("src"));
}
// 當前路徑下創建Pics文件夾
File file = new File("Pics");
file.mkdir();
String rootPath = file.getAbsolutePath();
System.out.println(">> 當前路徑:"+rootPath);
// 遍歷圖片並下載
int cnt = 1;
for (String str : URLS) {
System.out.println(">> 正在下載:"+str);
// 獲取response
Connection.Response imgRes = Jsoup.connect(str).ignoreContentType(true).execute();
FileOutputStream out = (new FileOutputStream(new java.io.File(file, cnt+".jpg")));
// imgRes.body() 就是圖片數據
out.write(imgRes.bodyAsBytes());
out.close();
cnt ++;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
喜歡的話,記得點“在看”哦~~↓↓↓