Java爬蟲-01快速入門 HttpClient+JSoup詳解 (附各種Demo)

Java爬蟲-01快速入門

HttpClient+JSoup詳解 (附各種Demo)

寫在前面:記錄了學習數據挖掘以來的學習歷程,先上之前的一些總結,隨着學習的加深會慢慢更新。

Java爬蟲-01快速入門  目錄

1.所需環境

2.HttpClient與Jsoup簡介

3.爲什麼要和JSoup共同使用?

4.項目maven依賴

5.HttpClient的入門使用

(1)簡介

(2)上一個簡單的示例Demo:

6.JSoup的入門使用

(1)主要類的簡介

(2)使用dom方法(遍歷一個Document對象)來查找元素 -不推薦

(3)使用選擇器語法來查找元素

(4)從元素抽取屬性,文本和HTML

(5)這個示例是當時看完某教程後的練手Demo:

7.一個完整的Demo

(1)GetResult類,用HttpClient來抓取頁面

(2)GetImg類,用Jsoup解析獲取的頁面

(3)測試單元test1.java


1.所需環境

我使用的環境是IDEA+Maven創建的maven project。其他方法亦可。

關於IDEA的下載安裝及使用,可以參考這裏:https://www.cnblogs.com/demingblog/p/5817125.html

關於Maven的下載安裝,官網下載地址:http://maven.apache.org/download.cgi

關於Maven的相關配置請參考:https://blog.csdn.net/cs4380/article/details/79158268

2.HttpClient與Jsoup簡介

1.什麼是HttpClient?

HttpClient 是Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。

2.爲什麼使用HttpClient?

HTTP 協議可能是現在 Internet 上使用得最多、最重要的協議了,越來越多的 Java 應用程序需要直接通過 HTTP 協議來訪問網絡資源。雖然在 JDK 的 java net包中已經提供了訪問 HTTP 協議的基本功能,但是對於大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。 

它的主要功能有:

(1) 實現了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)

(2) 支持自動轉向

(3) 支持 HTTPS 協議

(4) 支持代理服務器等

 3.什麼是JSoup?

jsoup是一款Java的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據。

它的主要功能有:

 (1) 從一個URL,文件或字符串中解析HTML;

(2) 使用DOM或CSS選擇器來查找、取出數據;

(3) 可操作HTML元素、屬性、文本; 

3.爲什麼要和JSoup共同使用?

httpClient 屬於專業的抓取網頁的庫,可以設置代理,抓取失敗可以重試抓取

在我的實際使用中,單獨用jsoup也可以直接抓取網頁,但是在抓取上,jsoup比較弱,API簡單,功能也簡單,主要是擴展htmlparser的功能吧,解析html。測試過程中jsoup抓取頁面經常報錯(time out等等)。

因此,我們可以用httpclient抓取網頁,再用Jsoup.parse解析頁面。

4.項目maven依賴

HttpClient 4.5.6:

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>

 JSoup 1.8.3

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.8.3</version>
</dependency>

5.HttpClient的入門使用

(1)簡介

使用HttpClient發送請求、接收響應很簡單,一般需要如下幾步即可。

1. 創建HttpClient對象。

2. 創建請求方法的實例,並指定請求URL。如果需要發送GET請求,創建HttpGet對象;如果需要發送POST請求,創建HttpPost對象。

3. 如果需要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對於HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數。

4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。

5. 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。

6. 釋放連接。無論執行方法是否成功,都必須釋放連接

(2)上一個簡單的示例Demo:

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

public class test {
    //聲明需要爬取的網址
    static String URL="http://www.baidu.com";

    //主函數入口
    public static void main(String args[]){
        //建立一個新的請求客戶端
        CloseableHttpClient httpClient=HttpClients.createDefault();

        //使用HttpGet的方式請求網址
        HttpGet httpGet = new HttpGet(URL);


        //獲取網址的返回結果
        CloseableHttpResponse response=null;
        try {
            response=httpClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //獲取返回結果中的實體
        HttpEntity entity = response.getEntity();

        //將返回的實體輸出
        try {
            System.out.println(EntityUtils.toString(entity));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6.JSoup的入門使用

我學習JSoup的過程中,這個作者的博客專欄提供了很大的幫助:專欄:使用JSOUP實現網絡爬蟲

同時,可以參考jsoup Cookbook(中文版),也非常的棒。

這裏就根據CookBook來對jsoup的使用進行歸納

(1)主要類的簡介

JSoup API中有6個包提供用於開發jsoup應用程序的類和接口。包中有很多類。

我們常用到的主要類:Jsoup、Document、Element/Elements

1.Document類:

是Jsoup的HTML文檔對象模型,它由很多節點組成。

Document將html文檔(String類型)解析爲很多的Element和TextNode對象,其中TextNode繼承自Node對象

繼承鏈:

Document extends Element extends Node

TextNode extends Node

2.Element/Elements類:

Element類是Node的直接子類,它表示由一個標籤名,多個屬性和子節點組成的html元素。從這個元素中,你可以提取數據,可以遍歷節點樹,可以操縱html。

注:Node是節點的抽象模型。Elements, Documents, Comments等都是節點的實例。

Elements對象類似一個由多個Element對象組成的集合,有一接口爲List<Element>,可以使用Element.select()方法去得到Elements 對象。

注:判斷Elements對象是否爲空需要.isEmpty()方法,而Element可以用==null

注:Elements.select()不能得到Element對象

3.Jsoup類:

通常用來建立連接,獲取響應或發送響應

一個簡單示例:

Document doc = Jsoup.connect(url).timeout(2000).get();

(2)使用dom方法(遍歷一個Document對象)來查找元素 -不推薦

Elements這個對象提供了一系列類似於DOM的方法來查找元素,抽取並處理其中的數據。

1.查找元素
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key) (and related methods)
Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling()
Graph: parent(), children(), child(int index)

2.元素數據
attr(String key)獲取屬性attr(String key, String value)設置屬性
attributes()獲取所有屬性
id(), className() and classNames()
text()獲取文本內容text(String value) 設置文本內容
html()獲取元素內HTMLhtml(String value)設置元素內的HTML內容
outerHtml()獲取元素外HTML內容
data()獲取數據內容(例如:script和style標籤)
tag() and tagName()

3.操作HTML和文本
append(String html), prepend(String html)
appendText(String text), prependText(String text)
appendElement(String tagName), prependElement(String tagName)
html(String value)

                                                                                                                                                              引用自CookBook-dom

(3)使用選擇器語法來查找元素

jsoup與其他解析器的區別就是可以使用類似jquery的選擇器語法來搜索及過濾出所需的元素

這裏我們還要介紹最重要的選擇器語法

jsoup elements對象支持類似於CSS (或jquery)的選擇器語法,來實現非常強大和靈活的查找功能。.

這個select 方法在Document, Element,或Elements對象中都可以使用。且是上下文相關的,因此可實現指定元素的過濾,或者鏈式選擇訪問。

Select方法將返回一個Elements集合,並提供一組方法來抽取和處理結果。

1.Selector選擇器概述
tagname: 通過標籤查找元素,比如:a
ns|tag: 通過標籤在命名空間查找元素,比如:可以用 fb|name 語法來查找 <fb:name> 元素
#id: 通過ID查找元素,比如:#logo
.class: 通過class名稱查找元素,比如:.masthead
[attribute]: 利用屬性查找元素,比如:[href]
[^attr]: 利用屬性名前綴來查找元素,比如:可以用[^data-] 來查找帶有HTML5 Dataset屬性的元素
[attr=value]: 利用屬性值來查找元素,比如:[width=500]
[attr^=value], [attr$=value], [attr*=value]: 利用匹配屬性值開頭、結尾或包含屬性值來查找元素,比如:[href*=/path/]
[attr~=regex]: 利用屬性值匹配正則表達式來查找元素,比如: img[src~=(?i)\.(png|jpe?g)]
*: 這個符號將匹配所有元素
Selector選擇器組合使用
el#id: 元素+ID,比如: div#logo
el.class: 元素+class,比如: div.masthead
el[attr]: 元素+class,比如: a[href]
任意組合,比如:a[href].highlight
ancestor child: 查找某個元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素
parent > child: 查找某個父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body標籤下所有直接子元素
siblingA + siblingB: 查找在A元素之前第一個同級元素B,比如:div.head + div
siblingA ~ siblingX: 查找A元素之前的同級X元素,比如:h1 ~ p
el, el, el:多個選擇器組合,查找匹配任一選擇器的唯一元素,例如:div.masthead, div.logo
2.僞選擇器selectors
:lt(n): 查找哪些元素的同級索引值(它的位置在DOM樹中是相對於它的父節點)小於n,比如:td:lt(3) 表示小於三列的元素
:gt(n):查找哪些元素的同級索引值大於n,比如: div p:gt(2)表示哪些div中有包含2個以上的p元素
:eq(n): 查找哪些元素的同級索引值與n相等,比如:form input:eq(1)表示包含一個input標籤的Form元素
:has(seletor): 查找匹配選擇器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素
:not(selector): 查找與選擇器不匹配的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表
:contains(text): 查找包含給定文本的元素,搜索不區分大不寫,比如: p:contains(jsoup)
:containsOwn(text): 查找直接包含給定文本的元素
:matches(regex): 查找哪些元素的文本匹配指定的正則表達式,比如:div:matches((?i)login)
:matchesOwn(regex): 查找自身包含文本匹配指定正則表達式的元素
注意:上述僞選擇器索引是從0開始的,也就是說第一個元素索引值爲0,第二個元素index爲1等*/

                                                                                                                                                          引用自CookBook-選擇器

select()的使用還可以參考這一篇博文,寫的也很好。

(4)從元素抽取屬性,文本和HTML

在解析獲得一個Document實例對象,並查找到一些元素之後,如何取得在這些元素中的數據呢?

方法
要取得一個屬性的值,可以使用Node.attr(String key) 方法
對於一個元素中的文本,可以使用Element.text()方法
對於要取得元素或屬性中的HTML內容,可以使用Element.html(), 或 Node.outerHtml()方法


示例:
String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
Document doc = Jsoup.parse(html);//解析HTML字符串返回一個Document實現
Element link = doc.select("a").first();//查找第一個a元素

String text = doc.body().text(); // "An example link"//取得字符串中的文本
String linkHref = link.attr("href"); // "http://example.com/"//取得鏈接地址
String linkText = link.text(); // "example""//取得鏈接地址中的文本

String linkOuterH = link.outerHtml();
    // "<a href="http://example.com"><b>example</b></a>"
String linkInnerH = link.html(); // "<b>example</b>"//取得鏈接內的html內容


tips:
上述方法是元素數據訪問的核心辦法。此外還其它一些方法可以使用:

Element.id()
Element.tagName()
Element.className() and Element.hasClass(String className)
這些訪問器方法都有相應的setter方法來更改數據.

                                                                                                                                                       引用自CookBook-抽取元素

(5)這個示例是當時看完某教程後的練手Demo:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;

class test2{
    public static void main(String args[]){
        //設置需要爬取的網頁,這裏爲方便起見就直接用Jsoup自帶的api來爬取網頁了
        //這個網頁是桂林電子科技大的信息科技學院的學院新聞版塊頁面
        String url = "http://www.guit.edu.cn/xwzx/mtxk.htm";
        //聲明Document類,來存儲爬取到的html文檔
        Document doc = null;
        try {
            doc = Jsoup.connect(url).timeout(2000).get();
            //調用Jsoup類中的connect()方法,url爲需要爬取的頁面
            //timeout()來設置超時時間,get()方法來獲取響應頁面
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //System.out.println(doc);//測試用

        //使用select選擇器
        Elements elements = doc.select(".box-list").select(".oh").select("a");

        //System.out.println(elements);//測試用

        for(Element e:elements){
            if(e.text().length()>8){
                //逐條輸出新聞信息
                System.out.println(e.text());
            }
        }

    }
}

7.一個完整的Demo

當時寫這個Demo時,這篇博文給了我很大幫助。我對其中的代碼加以完善和詳細註釋,附在下面。

(1)GetResult類,用HttpClient來抓取頁面

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

public class GetResult {
    public static String getResult(String url) throws Exception {
        //這裏用了try-with-resource語法,在try()括號中的資源會在try語句塊執行完之後自動釋放
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
             CloseableHttpResponse response = httpClient.execute(new HttpGetConfig(url))) 
        {
            String result = EntityUtils.toString(response.getEntity());
            return result;
        } catch (Exception e) {
            System.out.println("獲取失敗");
            return "";
        }
        //所以不需要再finally中釋放資源。
    }
}
//內部類,繼承HttpGet,爲了設置請求超時的參數
class HttpGetConfig extends HttpGet {
    public HttpGetConfig(String url) {
        super(url);
        setDefaulConfig();
    }

    private void setDefaulConfig() {
        this.setConfig(RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(10000)
                .setSocketTimeout(10000).build());
        this.setHeader("User-Agent", "spider");
    }
}

(2)GetImg類,用Jsoup解析獲取的頁面

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.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

public class GetImg {
    public class GetImg {
    public GetImg(String url) throws Exception{
        //獲取工具類GetResult返回的html,並用Jsoup解析
        String result = GetResult.getResult(url);
        Document document = Jsoup.parse(result);
        //若HTML文檔包含相對URLs路徑,需要將這些相對路徑轉換成絕對路徑的URLs
        document.setBaseUri(url);//指定base URI

        //獲取所有的img元素
        Elements elements = document.select("img");
        int i=1;
        for (Element e : elements) {
            //獲取每個src的絕對路徑
            String src = e.absUrl("src");
            URL urlSource = new URL(src);
            URLConnection urlConnection = urlSource.openConnection();

            //設置圖片名字
            String imageName = src.substring(src.lastIndexOf("/") + 1,i++);

            //控制檯輸出圖片的src
            System.out.println(e.absUrl("src"));

            //通過URLConnection得到一個流,將圖片寫到流中,並且新建文件保存
            InputStream in = urlConnection.getInputStream();
            OutputStream out = new FileOutputStream(new File("E:\\IDEA\\imgs\\", imageName));
            byte[] buf = new byte[1024];
            int l = 0;
            while ((l = in.read(buf)) != -1) {
                out.write(buf, 0, l);
            }
        }
    }
}

想要了解更多關於URL的處理,可以查看CookBook-URL

(3)測試單元test1.java

import org.junit.Test;

/**
 * 2018-9-8 單元測試
 * @author ljx
 */
public class test1 {
    @Test
    public void testGetResult() throws Exception{
        GetImg getImg = new GetImg("https://www.bilibili.com/");
    }
}

關於JUnit我這裏就不班門弄斧了,也是正在學習,想要了解的轉這裏。 

 

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