爬蟲第一天

今日課程的主要內容

  • 介紹爬蟲的基本概念
    • 爬蟲是什麼
    • 爬蟲的價值
    • 爬蟲的分類
    • 爬蟲的運行原理
  • 爬蟲的三大模塊
    • 爬蟲的第一大模塊: 獲取數據
      • 原生JDK的方式獲取網頁數據(理解)
        • get方法
        • post方式
      • 使用httpClient獲取網頁請求(重點)
        • get方式
        • post方式
    • 爬蟲的第二大模塊: 解析數據
      • 使用原生DOM操作(瞭解)
        • 對js中相關方法的回顧
      • 使用jsoup解析數據(重點)
        • 對選擇器的使用
    • 爬蟲的第三大模塊: 保存數據
      • 使用JDBCTemplate完成數據的保存工作(mysql)
  • 完成一個爬取起點中文網小說的爬蟲程序
  • 使用爬蟲完成用戶登錄,獲取登陸後的數據

1. 爬蟲的基本概念

1.1 什麼是爬蟲?

​ 網絡爬蟲是一種按照一定的規則,自動地抓取萬維網信息的程序 , 通常由三個模塊組成: 獲取數據, 解析數據, 保存數據

1.2爬蟲有什麼價值呢?

  • 爬蟲就是用來獲取互聯網的數據的, 數據就是無價之寶,一旦獲取了海量的數據後, 我們就可以對數據進行分類和分析,最終可以做一個網站用來展示分析後的數據: 比如 競價網站 或者 提供用戶各個維度的分析等
  • 總結:
    • 爬蟲的價值就是獲取數據的價值
    • 數據的價值,取決於獲取的數據內容的重要性(比如, 獲取一個網站上每一個用戶的購買記錄)

1.3 爬蟲的分類

  • 爬蟲主要分爲兩大類:
    • 一類: 通用型爬蟲
      • 通用型爬蟲主要是用來爬蟲互聯網上的所有的數據, 不限制行業, 不限制分類,這種爬蟲被稱爲是通用型的爬蟲, 例如 百度 就是一個巨大的通用型爬蟲了
    • 一類: 垂直爬蟲
      • 垂直爬蟲主要是用來獲取網站上某一個分類, 或者是某一行業的數據, 這種爬蟲被爲是垂直爬蟲, 例如: 慢慢買(商城網站)
  • 那麼一般我們需要開發那種類型的爬蟲呢?
    • 答案: 是垂直爬蟲, 在開發中, 我們大部分是針對的是某一個行業,或者是某個分類的內容進行獲取, 再加上通用型爬蟲已經非常的發達了, 例如百度, 谷歌

1.4 爬蟲的運行原理

  • 清楚爬蟲的作用:

    • 用來獲取網絡上的數據資源的
  • 爬蟲的基本流程:

      1. 指定爬蟲要獲取url
      1. 發送http請求獲取網頁數據
      1. 解析網頁數據
      1. 保存數據

2. 爬蟲的第一大模塊: 獲取數據

  • 獲取數據需要使用到HTTP請求

2.1 回顧http請求:

  • http的組成:
    • 請求:
      • 請求行
        • 兩種請求方式: get 和post
          • get和post的區別:
            • 參數放置的位置不同: get存放在url上, 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(未滿足期望值)
            服務器未滿足”期望”請求標頭字段的要求。
        • 5xx(服務器錯誤)
          這些狀態碼錶示服務器在處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。
          • 500(服務器內部錯誤)
            服務器遇到錯誤,無法完成請求。
          • 501(尚未實施)
            服務器不具備完成請求的功能。例如,服務器無法識別請求方法時可能會返回此代碼。
          • 502(錯誤網關)
            服務器作爲網關或代理,從上游服務器收到無效響應。
          • 503(服務不可用)
            服務器目前無法使用(由於超載或停機維護)。通常,這只是暫時狀態。
          • 504(網關超時)
            服務器作爲網關或代理,但是沒有及時從上游服務器收到請求。
            *505(HTTP 版本不受支持)
    • 響應頭
      • 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方式和選擇器的方式來進行數據的獲取
    • 如何使用呢?
        1. 導入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獲取新聞內容: 網易新聞

    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
  • 實現步驟:
      1. 指定url
      1. 獲取httpclient請求
      1. 創建請求方式, 使用post
      • 3.1) 封裝登陸的參數
      1. 發起請求,獲取response對象
      • 4.1) 獲取狀態碼,
      • 4.2) 根據狀態碼判斷, 如果是302,表示登陸成功, 獲取登陸成功後重定向後的url
      • 4.3) 獲取登陸成功後的cookie信息
      1. 根據重定向的url,重新發起一個請求, 獲取登陸成功後的頁面
      • 注意:重新發起請求式, 需要將登陸成功後的cookie的內容, 傳入當前的請求中
      1. 獲取登陸成功的頁面即可
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"));

        }

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章