今日課程的主要內容
- 介紹爬蟲的基本概念
- 爬蟲是什麼
- 爬蟲的價值
- 爬蟲的分類
- 爬蟲的運行原理
- 爬蟲的三大模塊
- 爬蟲的第一大模塊: 獲取數據
- 原生JDK的方式獲取網頁數據(理解)
- get方法
- post方式
- 使用httpClient獲取網頁請求(重點)
- get方式
- post方式
- 原生JDK的方式獲取網頁數據(理解)
- 爬蟲的第二大模塊: 解析數據
- 使用原生DOM操作(瞭解)
- 對js中相關方法的回顧
- 使用jsoup解析數據(重點)
- 對選擇器的使用
- 使用原生DOM操作(瞭解)
- 爬蟲的第三大模塊: 保存數據
- 使用JDBCTemplate完成數據的保存工作(mysql)
- 爬蟲的第一大模塊: 獲取數據
- 完成一個爬取起點中文網小說的爬蟲程序
- 使用爬蟲完成用戶登錄,獲取登陸後的數據
1. 爬蟲的基本概念
1.1 什麼是爬蟲?
網絡爬蟲是一種按照一定的規則,自動地抓取萬維網信息的程序 , 通常由三個模塊組成: 獲取數據, 解析數據, 保存數據
1.2爬蟲有什麼價值呢?
- 爬蟲就是用來獲取互聯網的數據的, 數據就是無價之寶,一旦獲取了海量的數據後, 我們就可以對數據進行分類和分析,最終可以做一個網站用來展示分析後的數據: 比如 競價網站 或者 提供用戶各個維度的分析等
- 總結:
- 爬蟲的價值就是獲取數據的價值
- 數據的價值,取決於獲取的數據內容的重要性(比如, 獲取一個網站上每一個用戶的購買記錄)
1.3 爬蟲的分類
- 爬蟲主要分爲兩大類:
- 一類: 通用型爬蟲
- 通用型爬蟲主要是用來爬蟲互聯網上的所有的數據, 不限制行業, 不限制分類,這種爬蟲被稱爲是通用型的爬蟲, 例如 百度 就是一個巨大的通用型爬蟲了
- 一類: 垂直爬蟲
- 垂直爬蟲主要是用來獲取網站上某一個分類, 或者是某一行業的數據, 這種爬蟲被爲是垂直爬蟲, 例如: 慢慢買(商城網站)
- 一類: 通用型爬蟲
- 那麼一般我們需要開發那種類型的爬蟲呢?
- 答案: 是垂直爬蟲, 在開發中, 我們大部分是針對的是某一個行業,或者是某個分類的內容進行獲取, 再加上通用型爬蟲已經非常的發達了, 例如百度, 谷歌
1.4 爬蟲的運行原理
-
清楚爬蟲的作用:
- 用來獲取網絡上的數據資源的
-
爬蟲的基本流程:
-
- 指定爬蟲要獲取url
-
- 發送http請求獲取網頁數據
-
- 解析網頁數據
-
- 保存數據
-
2. 爬蟲的第一大模塊: 獲取數據
- 獲取數據需要使用到HTTP請求
2.1 回顧http請求:
- http的組成:
- 請求:
- 請求行
- 兩種請求方式: get 和post
- get和post的區別:
- 參數放置的位置不同: get存放在url上, post存放在請求體重
- 數據量不同: get是有數據量限制的, 不能傳輸大量的數據, post沒有限制
- 是否有請求體: get是沒有請求體, post是有請求體
- get和post的區別:
- 兩種請求方式: get 和post
- 請求頭
- user-agent: 設置請求的瀏覽器的版本信息
- cookie: 將本地緩存該服務器的cookie, 發送給服務端
- 請求體
- 如果使用post請求, 需要向請求體放置請求參數
- 請求行
- 響應:
- 響應行
- Http請求狀態碼:
- 1xx(臨時響應):表示臨時響應並需要請求者繼續執行操作的狀態碼。
- 100(繼續)請求者應當繼續提出請求。服務器返回此代碼表示已收到請求的第一部 分,正在等待其餘部分。
- 101(切換協議)請求者已要求服務器切換協議,服務器已確認並準備切換。
- 2xx(成功) 表示成功處理了請求的狀態碼。
- 200:響應成功
- 201(已創建)請求成功並且服務器創建了新的資源。
- 202(已接受)服務器已接受請求,但尚未處理。
- 203(非授權信息)服務器已成功處理了請求,但返回的信息可能來自另一來源。
- 204(無內容)服務器成功處理了請求,但沒有返回任何內容。
- 205(重置內容)服務器成功處理了請求,但沒有返回任何內容。與 204響應不同,此響應要求請求者重置文檔視圖(例如,清除表單內容以輸入新內容)。
- 206(部分內容)
服務器成功處理了部分 GET 請求。
- 3xx(重定向)
要完成請求,需要進一步操作。通常,這些狀態碼用來重定向。Google 建議您在每次請求中使用重定向不要超過 5次。您可以使用網站管理員工具查看一下 Googlebot 在抓取重定向時是否遇到問題。診斷下的網絡抓取頁列出了由於重定向錯誤導致Googlebot 無法抓取的網址。- 300(多種選擇)
針對請求,服務器可執行多種操作。服務器可根據請求者 (user agent) 選擇一項操作,或提供操作列表供請求者選擇。
*301(永久移動)
請求的網頁已永久移動到新位置。服務器返回此響應(對 GET 或 HEAD請求的響應)時,會自動將請求者轉到新位置。您應使用此代碼告訴 Googlebot 某個網頁或網站已永久移動到新位置。 - 302(臨時移動)
服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的301 代碼類似,會自動將請求者轉到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個網頁或網站已經移動,因爲Googlebot 會繼續抓取原有位置並編制索引。 - 303(查看其他位置)
請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。對於除 HEAD之外的所有請求,服務器會自動轉到其他位置。 - 304(未修改)
自從上次請求後,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。
如果網頁自請求者上次請求後再也沒有更改過,您應將服務器配置爲返回此響應(稱爲 If-Modified-Since HTTP標頭)。服務器可以告訴 Googlebot 自從上次抓取後網頁沒有變更,進而節省帶寬和開銷。 - 305(使用代理)
請求者只能使用代理訪問請求的網頁。如果服務器返回此響應,還表示請求者應使用代理。 - 307(臨時重定向)
服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的301代碼類似,會自動將請求者轉到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個頁面或網站已經移動,因爲 Googlebot會繼續抓取原有位置並編制索引。 - 4xx(請求錯誤)
這些狀態碼錶示請求可能出錯,妨礙了服務器的處理。- 400(錯誤請求)
服務器不理解請求的語法。 - 401(未授權)
請求要求身份驗證。對於登錄後請求的網頁,服務器可能返回此響應。 - 403(禁止)
服務器拒絕請求。如果您在 Googlebot 嘗試抓取您網站上的有效網頁時看到此狀態碼(您可以在 Google網站管理員工具診斷下的網絡抓取頁面上看到此信息),可能是您的服務器或主機拒絕了 Googlebot 訪問。 - 404(未找到)
服務器找不到請求的網頁。例如,對於服務器上不存在的網頁經常會返回此代碼。
如果您的網站上沒有 robots.txt 文件,而您在 Google 網站管理員工具“診斷”標籤的 robots.txt頁上看到此狀態碼,則這是正確的狀態碼。但是,如果您有 robots.txt 文件而又看到此狀態碼,則說明您的 robots.txt文件可能命名錯誤或位於錯誤的位置(該文件應當位於頂級域,名爲 robots.txt)。
如果對於 Googlebot 抓取的網址看到此狀態碼(在”診斷”標籤的 HTTP 錯誤頁面上),則表示 Googlebot跟隨的可能是另一個頁面的無效鏈接(是舊鏈接或輸入有誤的鏈接)。
*405(方法禁用)
禁用請求中指定的方法。 - 406(不接受)
無法使用請求的內容特性響應請求的網頁。 - 407(需要代理授權)
此狀態碼與 401(未授權)類似,但指定請求者應當授權使用代理。如果服務器返回此響應,還表示請求者應當使用代理。 - 408(請求超時)
服務器等候請求時發生超時。 - 409(衝突)
服務器在完成請求時發生衝突。服務器必須在響應中包含有關衝突的信息。服務器在響應與前一個請求相沖突的 PUT請求時可能會返回此代碼,以及兩個請求的差異列表。 - 410(已刪除)
如果請求的資源已永久刪除,服務器就會返回此響應。該代碼與404(未找到)代碼類似,但在資源以前存在而現在不存在的情況下,有時會用來替代 404 代碼。如果資源已永久移動,您應使用 301指定資源的新位置。 - 411(需要有效長度)
服務器不接受不含有效內容長度標頭字段的請求。 - 412(未滿足前提條件)
服務器未滿足請求者在請求中設置的其中一個前提條件。 - 413(請求實體過大)
服務器無法處理請求,因爲請求實體過大,超出服務器的處理能力。 - 414(請求的 URI 過長)
請求的 URI(通常爲網址)過長,服務器無法處理。 - 415(不支持的媒體類型)
請求的格式不受請求頁面的支持。 - 416(請求範圍不符合要求)
如果頁面無法提供請求的範圍,則服務器會返回此狀態碼。 - 417(未滿足期望值)
服務器未滿足”期望”請求標頭字段的要求。
- 400(錯誤請求)
- 5xx(服務器錯誤)
這些狀態碼錶示服務器在處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。- 500(服務器內部錯誤)
服務器遇到錯誤,無法完成請求。 - 501(尚未實施)
服務器不具備完成請求的功能。例如,服務器無法識別請求方法時可能會返回此代碼。 - 502(錯誤網關)
服務器作爲網關或代理,從上游服務器收到無效響應。 - 503(服務不可用)
服務器目前無法使用(由於超載或停機維護)。通常,這只是暫時狀態。 - 504(網關超時)
服務器作爲網關或代理,但是沒有及時從上游服務器收到請求。
*505(HTTP 版本不受支持)
- 500(服務器內部錯誤)
- 300(多種選擇)
- 1xx(臨時響應):表示臨時響應並需要請求者繼續執行操作的狀態碼。
- 響應頭
- Location: 響應的需要跳轉的頁面
- set-cookie: 向瀏覽器發送cookie
- 響應體
- 響應回來的頁面, 需要獲取到
- 請求:
2.2 使用原生JDK完成http請求
- 使用get方式:
public class JdkGet {
public static void main(String[] args) throws Exception {
// 1.指定一個url
String domain = "http://www.itcast.cn";
// 2.打開一個連接
URL url = new URL(domain);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 3.如果是一個get請求,需要設置請求頭信息
// 請求方式,必須是大寫
conn.setRequestMethod("GET");
// 4.發起請求
// 當用戶調用getInputStream,其實發送一個請求,並獲得了一個輸入流。
InputStream inputStream = conn.getInputStream();
// 5.打印數據
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream, Charset.forName("utf-8")));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
}
- 使用post方式:
public class JDKPost {
public static void main(String[] args) throws Exception {
//1. 指定URL路徑
String url = "http://www.itcast.cn";
// 2. 創建URL對象
URL urlObj = new URL(url);
//3. 打開一個連接
HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
//3.1 指定請求方式
connection.setRequestMethod("POST");// 注意 這裏使用的POST連接
//3.2 打開輸出流
connection.setDoOutput(true);
//3.3 封裝參數
OutputStream outputStream = connection.getOutputStream();
outputStream.write("username=張三&password=123".getBytes());
//4. 獲取輸入流
InputStream in = connection.getInputStream();
//5. 打印數據
byte[] b = new byte[1024];
int len;
while((len=in.read(b))!=-1) {
System.out.println(new String(b,0,len));
}
in.close();
outputStream.close();
}
}
- 小結
- 使用post獲取時, 由於需要將參數通過請求體發送, 此時就需要一個輸出流, 而原生的JDK方式默認是將輸出流關閉的, 所以需要需要手動設置打開, 並制定請求方式
//3.1 指定請求方式
connection.setRequestMethod("POST");// 注意 這裏使用的POST連接
//3.2 打開輸出流
connection.setDoOutput(true);
//3.3 封裝參數
OutputStream outputStream = connection.getOutputStream();
outputStream.write("username=張三&password=123".getBytes());
2.3 使用httpclient完成http請求
- get方式
public static void main(String[] args) throws Exception {
//1. 指定url
String url = "http://www.itcast.cn";
//2. 指定發送方式, get
HttpGet httpGet = new HttpGet(url);
//3. 獲取httpclient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
//4. 執行請求
CloseableHttpResponse response = httpClient.execute(httpGet);
//5. 獲取網頁數據
int status = response.getStatusLine().getStatusCode();
if(status==200) {
String html = EntityUtils.toString(response.getEntity(),Charset.forName("utf-8"));
System.out.println(html);
}
response.close();
httpClient.close();
}
- post方式
public static void main(String[] args) throws Exception{
//1. 指定url
String url = "http://www.itcast.cn";
//2. 指定發送方式 post
HttpPost httpPost = new HttpPost(url);
//3. 獲取httpclient實例, 用來執行請求
CloseableHttpClient httpClient = HttpClients.createDefault();
//3.1 封裝參數
List <NameValuePair> parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("userName", "張三"));
parameters.add(new BasicNameValuePair("password", "123"));
HttpEntity entity = new UrlEncodedFormEntity(parameters);
httpPost.setEntity(entity );
//4. 執行請求
CloseableHttpResponse response = httpClient.execute(httpPost);
//5. 獲取數據
int statusCode = response.getStatusLine().getStatusCode();//獲取狀態碼
if(statusCode==200) {
String html = EntityUtils.toString(response.getEntity());
System.out.println(html);
}
}
- 小結
- 使用httpclient發送請求, 需要獲取CloseableHttpClient用來執行請求
- 如果要發送get請求, 需要獲取httpGet對象
- 如果需要發送post請求, 需要獲取httpPost對象
- 如果post需要進行封裝參數, 需要使用UrlEncodedFormEntity 進行數據封裝
3. 爬蟲的第二大模塊: 解析數據
- 解析數據, 其本質上就是解析網頁, 如何獲取網頁的數據呢?
- js中提供了DOM操作的, 將一個頁面看做是一個document文檔, 然後從文檔中獲取數據
###3.1 Document 對象集合
集合 | 描述 |
---|---|
[all] | 提供對文檔中所有 HTML 元素的訪問。 |
[anchors] | 返回對文檔中所有 Anchor 對象的引用。 |
applets | 返回對文檔中所有 Applet 對象的引用。 |
[forms] | 返回對文檔中所有 Form 對象引用。 |
[images] | 返回對文檔中所有 Image 對象引用。 |
[links] | 返回對文檔中所有 Area 和 Link 對象引用。 |
###3.2 Document 對象屬性
屬性 | 描述 |
---|---|
body | 提供對 元素的直接訪問。 對於定義了框架集的文檔,該屬性引用最外層的 。 |
cookie | 設置或返回與當前文檔有關的所有 cookie。 |
domain | 返回當前文檔的域名。 |
lastModified | 返回文檔被最後修改的日期和時間。 |
referrer | 返回載入當前文檔的文檔的 URL。 |
title | 返回當前文檔的標題。 |
URL | 返回當前文檔的 URL。 |
###3.3 Document 對象方法
方法 | 描述 |
---|---|
close() | 關閉用 document.open() 方法打開的輸出流,並顯示選定的數據。 |
getElementById() | 返回對擁有指定 id 的第一個對象的引用。 |
getElementsByName() | 返回帶有指定名稱的對象集合。 |
getElementsByTagName() | 返回帶有指定標籤名的對象集合。 |
open() | 打開一個流,以收集來自任何 document.write() 或 document.writeln() 方法的輸出。 |
write() | 向文檔寫 HTML 表達式 或 JavaScript 代碼。 |
writeln() |
- 在java中如何去操作呢? 使用jsoup
- jsoup: 一款專爲解析網頁數據而生的技術, 可以通過一套比較省力的API來操作網頁, jsoup提供提供了基於document的解析方式, 可以使用原生的document方式和選擇器的方式來進行數據的獲取
- 如何使用呢?
-
- 導入maven依賴
-
<dependency>
<!-- jsoup HTML parser library @ https://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.3</version>
</dependency>
2) 獲取document對象
public static void main(String[] args) throws IOException {
//獲取document的方式
//1. 通過url路徑獲取
// Document document = Jsoup.connect("http://www.itcast.cn").get();
// Document document = Jsoup.connect("http://www.itcast.cn").post();
//2. 通過HTML代碼片段獲取
// String html = "<li class=\"a_gd innl\"><a href=\"http://www.itcast.cn/subject/javaeezly/index.shtml\" target=\"_blank\"><img src=\"/2018czgw/images/icon_fuli1.jpg\"> <span>學科介紹</span> </a></li>";
//Document document = Jsoup.parse(html);
//3. 通過讀取本地的HTML文件的方式
Jsoup.parse(new File("c:/a.txt"),"utf-8");
}
3) 解析網頁 : 先使用原生的api操作
public static void main(String[] args) throws IOException {
//解析 此 http://www.itcast.cn/subject/uizly/index.shtml
String url = "http://www.itcast.cn/subject/uizly/index.shtml";
Document document = Jsoup.connect(url).get();
//開始解析
//獲取網頁的標題
String title = document.title();
System.out.println(title);
//獲取網頁 獲取 class屬性爲inner的標籤
Elements divInner = document.getElementsByClass("inner");
//獲取div下的li標籤
Elements li = divInner.get(1).getElementsByTag("li");
System.out.println(li);
}
4) 解析網頁: 使用選擇器的方式:
說明: jsoup中所使用的選擇器和css的選擇器以及jquery的選擇器是一樣的, 所以可以直接參考css和jquery的選擇器進行使用即可
選擇器 | 例子 | 例子描述 | CSS |
---|---|---|---|
.class | .intro | 選擇 class=“intro” 的所有元素。 | 1 |
#id | #firstname | 選擇 id=“firstname” 的所有元素。 | 1 |
* | * | 選擇所有元素。 | 2 |
element | p | 選擇所有 元素。 |
1 |
element,element | div,p | 選擇所有 元素和所有 元素。 |
1 |
element element | div p | 選擇 元素內部的所有 元素。 |
1 |
element>element | div>p | 選擇父元素爲 元素的所有 元素。 |
2 |
element+element | div+p | 選擇緊接在 元素之後的所有 元素。 |
2 |
[attribute] | [target] | 選擇帶有 target 屬性所有元素。 | 2 |
[attribute=value] | [target=_blank] | 選擇 target="_blank" 的所有元素。 | 2 |
[attribute~=value] | [title~=flower] | 選擇 title 屬性包含單詞 “flower” 的所有元素。 | 2 |
[attribute|=value] | [lang|=en] | 選擇 lang 屬性值以 “en” 開頭的所有元素。 | 2 |
:link | a:link | 選擇所有未被訪問的鏈接。 | 1 |
:visited | a:visited | 選擇所有已被訪問的鏈接。 | 1 |
:active | a:active | 選擇活動鏈接。 | 1 |
:hover | a:hover | 選擇鼠標指針位於其上的鏈接。 | 1 |
:focus | input:focus | 選擇獲得焦點的 input 元素。 | 2 |
:first-letter | p:first-letter | 選擇每個 元素的首字母。 |
1 |
:first-line | p:first-line | 選擇每個 元素的首行。 |
1 |
:first-child | p:first-child | 選擇屬於父元素的第一個子元素的每個 元素。 |
2 |
:before | p:before | 在每個 元素的內容之前插入內容。 |
2 |
:after | p:after | 在每個 元素的內容之後插入內容。 |
2 |
:lang(language) | p:lang(it) | 選擇帶有以 “it” 開頭的 lang 屬性值的每個 元素。 |
2 |
element1~element2 | p~ul | 選擇前面有 元素的每個
|
3 |
[attribute^=value] | a[src^=“https”] | 選擇其 src 屬性值以 “https” 開頭的每個 元素。 | 3 |
[attribute$=value] | a[src$=".pdf"] | 選擇其 src 屬性以 “.pdf” 結尾的所有 元素。 | 3 |
[attribute*=value] | a[src*=“abc”] | 選擇其 src 屬性中包含 “abc” 子串的每個 元素。 | 3 |
:first-of-type | p:first-of-type | 選擇屬於其父元素的首個 元素的每個 元素。 |
3 |
:last-of-type | p:last-of-type | 選擇屬於其父元素的最後 元素的每個 元素。 |
3 |
:only-of-type | p:only-of-type | 選擇屬於其父元素唯一的 元素的每個 元素。 |
3 |
:only-child | p:only-child | 選擇屬於其父元素的唯一子元素的每個 元素。 |
3 |
:nth-child(n) | p:nth-child(2) | 選擇屬於其父元素的第二個子元素的每個 元素。 |
3 |
:nth-last-child(n) | p:nth-last-child(2) | 同上,從最後一個子元素開始計數。 | 3 |
:nth-of-type(n) | p:nth-of-type(2) | 選擇屬於其父元素第二個 元素的每個 元素。 |
3 |
:nth-last-of-type(n) | p:nth-last-of-type(2) | 同上,但是從最後一個子元素開始計數。 | 3 |
:last-child | p:last-child | 選擇屬於其父元素最後一個子元素每個 元素。 |
3 |
:root | :root | 選擇文檔的根元素。 | 3 |
:empty | p:empty | 選擇沒有子元素的每個 元素(包括文本節點)。 |
3 |
:target | #news:target | 選擇當前活動的 #news 元素。 | 3 |
:enabled | input:enabled | 選擇每個啓用的 元素。 | 3 |
:disabled | input:disabled | 選擇每個禁用的 元素 | 3 |
:checked | input:checked | 選擇每個被選中的 元素。 | 3 |
:not(selector) | :not§ | 選擇非 元素的每個元素。 |
3 |
::selection | ::selection | 選擇被用戶選取的元素部分。 | 3 |
常用的選擇器:
id選擇器, class選擇器, 標籤選擇器, 後代選擇器, 屬性選擇器
3.3.1使用jsoup獲取傳智博客的課程分類信息
public static void main(String[] args) throws IOException {
//使用選擇器解析數據: www.itcast.cn
String url = "http://www.itcast.cn";
Document document = Jsoup.connect(url).get();
//需求解析出此網頁中的所有的學科
Elements div = document.select("div.nav_txt");
// System.out.println(div);
Elements tagA = div.select("ul li a");
// System.out.println(tagA);
for (Element element : tagA){
//獲取分類課程信息
String text = element.text();
System.out.println(text);
}
}
3.3.2獲取新聞內容: 網易新聞
- url: http://news.163.com/18/0714/14/DMMDKIM1000189FH.html
- 需求:獲取網易新聞中某個新聞的內容和其他的屬性
public static void main(String[] args) throws IOException {
//獲取網易中某一條新聞的內容: http://news.163.com/18/0714/14/DMMDKIM1000189FH.html
String url = "http://news.163.com/18/0714/14/DMMDKIM1000189FH.html";
//使用jsoup獲取document對象:
Document document = Jsoup.connect(url).get();
//開始解析網頁: 獲取網頁新聞的標題
Elements elements = document.select("#epContentLeft>h1");
String title = elements.text();
System.out.println(title);
//獲取新聞的正文
Elements p = document.select("#endText p");
for (Element element :p){
System.out.println(element.text());
}
//獲取新聞的來源地
Elements addr = document.select("div[class=ep-source cDGray] .left");
System.out.println(addr.text());
}
4. 爬蟲的第三大模塊: 保存數據
使用jdbcTemplate完成保存數據到mysql中即可, 後期可以使用hadoop或者hbase均可
##案例一: 讀取小說網站: 起點中文網
- url: https://www.qidian.com/
- 需求: 獲取起點中文的24小時的熱銷榜的書名, 讀取其中的一本書的免費章節
- 要求: 使用httpclient獲取網頁內容, 使用jsoup進行解析
public static void main(String[] args) throws IOException {
//讀取起點中文網的小說
//1. 指定url
String url = "https://www.qidian.com/";
//2. 獲取httpclient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
//3. 獲取請求方式
HttpGet httpGet = new HttpGet(url);
//4. 執行請求
CloseableHttpResponse response = httpClient.execute(httpGet);
//5. 獲取網頁
String html = EntityUtils.toString(response.getEntity(), "utf-8");
//6. 解析網頁
Document document = Jsoup.parse(html);
//獲取24小時熱門榜單
Elements time24H = document.select("div[class=rank-list sort-list]");
//獲取榜單的標題
String h3 = time24H.select("h3").text();
//System.out.println(h3);
//獲取書籍的列表
Elements bookList = time24H.select(".book-list li a[href*=book.qidian.com]");
//System.out.println(bookList);
//獲取書籍的url
for(Element element: bookList){
String href = element.attr("href");
//url拼接
String bookUrl ="https:"+href;
System.out.println(bookUrl+"讀取");
httpClient = HttpClients.createDefault();
httpGet = new HttpGet(bookUrl);
response = httpClient.execute(httpGet);
html = EntityUtils.toString(response.getEntity(), "utf-8");
document = Jsoup.parse(html);
String readHref = document.select("#readBtn").attr("href");
readHref = "https:"+readHref;
while(true) {
httpClient = HttpClients.createDefault();
httpGet = new HttpGet(readHref);
response = httpClient.execute(httpGet);
html = EntityUtils.toString(response.getEntity(), "utf-8");
//獲取小說章節的內容(第一章)
document = Jsoup.parse(html);
//獲取章節的名稱
String hread = document.select("div.text-head h3").text();
System.out.println(hread);
//獲取小說的內容
Elements contents = document.select("div[class=read-content j_readContent] p");
for (Element content : contents) {
String text = content.text();
//直接將小說內容打印, 也可以保存到數據庫或者是文本文件中
System.out.println(text);
}
//獲取下一章節的url
String nextHref = document.select("#j_chapterNext[href*=read.qidian.com]").attr("href");
if(nextHref==null || "".equals(nextHref) || " ".equals(nextHref)){
System.out.println("跳出----------------------------------------------------");
break;
}
readHref = "https:" + nextHref;
}
}
}
}
案例二: 模擬登陸,獲取數據
- 登陸的url http://www.svn.club/user/login
- 實現步驟:
-
- 指定url
-
- 獲取httpclient請求
-
- 創建請求方式, 使用post
- 3.1) 封裝登陸的參數
-
- 發起請求,獲取response對象
- 4.1) 獲取狀態碼,
- 4.2) 根據狀態碼判斷, 如果是302,表示登陸成功, 獲取登陸成功後重定向後的url
- 4.3) 獲取登陸成功後的cookie信息
-
- 根據重定向的url,重新發起一個請求, 獲取登陸成功後的頁面
- 注意:重新發起請求式, 需要將登陸成功後的cookie的內容, 傳入當前的請求中
-
- 獲取登陸成功的頁面即可
-
public class JDSpiderVersion1 {
public static void main(String[] args) throws Exception {
//1. 指定登錄的url
String url = "http://www.svn.club/user/login";
//2. 創建httpclient請求
CloseableHttpClient httpClient = HttpClients.createDefault();
//3. 創建請求方式, 由於需要進行參數的傳遞, 所以需要使用post請求
HttpPost httpPost = new HttpPost(url);
//3.1 封裝參數
ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
list.add(new BasicNameValuePair("uid","itcast"));
list.add(new BasicNameValuePair("pwd","www.itcast.cn"));
list.add(new BasicNameValuePair("x","109"));
list.add(new BasicNameValuePair("y","12"));
HttpEntity entity = new UrlEncodedFormEntity(list);
httpPost.setEntity(entity);
//4. 執行請求
CloseableHttpResponse response = httpClient.execute(httpPost);
//5. 判斷請求狀態碼
if(302==response.getStatusLine().getStatusCode()){
//獲取重定向的頁面
Header header = response.getHeaders("Location")[0];
Header[] headers = response.getHeaders("Set-Cookie");
String cookie = headers[0].getValue();
String reUrl = header.getValue();
//此時獲取到登錄成功後重定向的頁面, 接着進行url的拼接
System.out.println(reUrl);
url = "http://www.svn.club/my_project";
//重新發送請求, 獲取httpclient對象
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
//拼裝請求頭, 需要拼接登錄後保存在瀏覽器端的cookie
httpGet.setHeader("Cookie",cookie);
response = client.execute(httpGet);
//打印獲取的頁面
System.out.println(EntityUtils.toString(response.getEntity(),"utf-8"));
}
}
}