1. 程式人生 > >httpclient 學習

httpclient 學習

apache 提高 www. 著名 可靠的 最大 img via ice

Http協議的重要性相信不用我多說了,HttpClient相比傳統JDK自帶的URLConnection,增加了易用性和靈活性,它不僅是客戶端發送Http請求變得容易,而且也方便了開發人員測試接口(基於Http協議的),即提高了開發的效率,也方便提高代碼的健壯性。因此熟練掌握HttpClient是很重要的必修內容,掌握HttpClient後,相信對於Http協議的了解會更加深入。

一、簡介

HttpClient是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的支持HTTP協議的客戶端編程工具包,並且它支持HTTP協議最新的版本和建議。HttpClient已經應用在很多的項目中,比如Apache Jakarta上很著名的另外兩個開源項目Cactus和HTMLUnit都使用了HttpClient。

二、特性

1. 基於標準、純凈的Java語言。實現了Http1.0和Http1.1

2. 以可擴展的面向對象的結構實現了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。

3. 支持HTTPS協議。

4. 通過Http代理建立透明的連接。

5. 利用CONNECT方法通過Http代理建立隧道的https連接。

6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos認證方案。

7. 插件式的自定義認證方案。

8. 便攜可靠的套接字工廠使它更容易的使用第三方解決方案。

9. 連接管理器支持多線程應用。支持設置最大連接數,同時支持設置每個主機的最大連接數,發現並關閉過期的連接。

10. 自動處理Set-Cookie中的Cookie。

11. 插件式的自定義Cookie策略。

12. Request的輸出流可以避免流中內容直接緩沖到socket服務器。

13. Response的輸入流可以有效的從socket服務器直接讀取相應內容。

14. 在http1.0和http1.1中利用KeepAlive保持持久連接。

15. 直接獲取服務器發送的response code和 headers。

16. 設置連接超時的能力。

17. 實驗性的支持http1.1 response caching。

18. 源代碼基於Apache License 可免費獲取。

三、使用方法

使用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. 釋放連接。無論執行方法是否成功,都必須釋放連接

四、實例

實例一:模擬get請求發送,獲取返回的內容。

package httpclient;

import java.io.IOException;

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;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("https://www.cnblogs.com"); //創建get 請求
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            HttpEntity httpentity = response.getEntity(); //獲取響應
            System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

結果如下:

技術分享

說明上面代碼采用的maven 項目需要導入httpclient相關的包,pom文件如下:

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>

總結,上面我看看到httpclient 很容易的模擬了,客戶端發送了http請求,post方式方式一樣,但是接下來我們看下面的一個實例:

package httpclient;

import java.io.IOException;

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;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("http://www.tuicool.com/"); //創建get 請求
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            HttpEntity httpentity = response.getEntity(); //獲取響應
            System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

結果如下:

技術分享

這是為什麽呢? 原因就在於我們上面模擬的是客戶端發送了http請求,但不是模擬的瀏覽器發出的請求,因此有些網站做了防護,怎麽來模擬瀏覽器發出的請求呢? 瀏覽器在請的過程中,我們知道會有請求頭信息,以便目標服務器識別,如下:

技術分享

會有一個:

  1. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
  2. Accept-Encoding: gzip, deflate
  3. Accept-Language: zh-CN,zh;q=0.8
  4. Cache-Control: max-age=0
  5. Connection: keep-alive
  6. Cookie: _tuicool_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJWE5MWZjYzMwMDBmYjRjZWJjZWI5ZmE1MWNlNzQwNzQ4BjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMXpvZzZRVEVLNHNEYlJNYVJqMVlrUG5tUHJWR0FaeDh0RzFxY28za3pzR009BjsARkkiDnJldHVybl90bwY7AEZJIitodHRwOi8vd3d3LnR1aWNvb2wuY29tL2FydGljbGVzL2JRajJFMwY7AEY%3D--d83358350fdbe214e013c16660630e5ffa3fc4f0; UM_distinctid=15e47ec08fe321-044bcb8a12f122-546b3971-100200-15e47ec08ff33c; CNZZDATA5541078=cnzz_eid%3D626609411-1504443418-%26ntime%3D1504443418
  7. Host: www.tuicool.com
  8. If-None-Match: W/"a415838fed502ea413339b0b13542108"
  9. Upgrade-Insecure-Requests: 1
  10. User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36

其中最重要的是就是User-Agent,那麽我們要模擬瀏覽器,則需要設置頭消息,代碼如下:

package httpclient;

import java.io.IOException;

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;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("http://www.tuicool.com/"); //創建get 請求
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            HttpEntity httpentity = response.getEntity(); //獲取響應
            System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

這個時候,我們測試一下可以看到結果如下:

技術分享

結果已經正常顯示出來了,但這個時候,我們是否想到,既然一個瀏覽器發送了http請求,我們會看到有狀態,那麽如何通過httpclient返回的response來獲取對應的狀態呢,以及如何獲取響應的類型呢?類型即我們說的content-type

技術分享

這個時候我們需要通過HttpEntity 來獲取,為什麽獲取content-type 是因為獲取的類型非常多,有些是不需要我們采集的,這個時候我們可以通過這個內容類型進行采集過濾。

代碼如下:

package httpclient;

import java.io.IOException;

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;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("http://www.tuicool.com/"); //創建get 請求
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            System.out.println(response.getStatusLine().getStatusCode());
            HttpEntity httpentity = response.getEntity(); //獲取響應
            System.out.println(httpentity.getContentType().getValue());
            //System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

結果如下:

技術分享

總結,上面我們采取的都靜態的文本等,但是我們采集的時候,如果要采集圖片怎麽辦,圖片的獲取處理方式如下:

比如:http://aimg2.tuicool.com/qm6Rre6.jpg!index 采集這個圖片。

package httpclient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.FileUtils;
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;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("http://aimg2.tuicool.com/qm6Rre6.jpg!index"); //創建get 請求
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            //System.out.println(response.getStatusLine().getStatusCode());
            HttpEntity httpentity = response.getEntity(); //獲取響應
            if(httpentity != null)
            {
                System.out.println(httpentity.getContentType().getValue()); //判斷內容的類型,因為我們要采集圖片,所以要過濾掉其它內容.
                //接著,圖片肯定要通過流的方式去讀取.
                InputStream inputStream = httpentity.getContent();
                //然後通過輸入流,然後讀取流,輸出流,則可以轉換讀取我們的圖片,流的復制.
                //在這裏我們可以 通過Apache 提供的IO 工具流來直接進行流的復制.
                FileUtils.copyToFile(inputStream, new File("D://a.jpg")); //實際中是要拷貝真是的目錄下面,並且圖片的名稱也是唯一的。
            }
            //System.out.println(httpentity.getContentType().getValue());
            //System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

註意上面,需要導入io 包。

https://mvnrepository.com/artifact/commons-io/commons-io/2.5

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>

實例二 :上面我們演示了,圖片的采集,和靜態信息的采集,但是對於一些有防範的網站,一般大公司會有屏蔽信,即不讓你長時間采集,一旦你長時間采集,則會進行封掉IP,那這個時候我們該怎麽辦,這個時候我們需要代碼IP,先看下面的介紹:

在爬取網頁的時候,有的目標站點有反爬蟲機制,對於頻繁訪問站點以及規則性訪問站點的行為,會采集屏蔽IP措施。

這時候,代理IP就派上用場了。

關於代理IP的話 也分幾種 透明代理、匿名代理、混淆代理、高匿代理

1、透明代理(Transparent Proxy)

REMOTE_ADDR = Proxy IP

HTTP_VIA = Proxy IP

HTTP_X_FORWARDED_FOR = Your IP

透明代理雖然可以直接“隱藏”你的IP地址,但是還是可以從HTTP_X_FORWARDED_FOR來查到你是誰。

2、匿名代理(Anonymous Proxy)

REMOTE_ADDR = proxy IP

HTTP_VIA = proxy IP

HTTP_X_FORWARDED_FOR = proxy IP

匿名代理比透明代理進步了一點:別人只能知道你用了代理,無法知道你是誰。

還有一種比純匿名代理更先進一點的:混淆代理,見下節。

3、混淆代理(Distorting Proxies)

REMOTE_ADDR = Proxy IP

HTTP_VIA = Proxy IP

HTTP_X_FORWARDED_FOR = Random IP address

如上,與匿名代理相同,如果使用了混淆代理,別人還是能知道你在用代理,但是會得到一個假的IP地址,偽裝的更逼真:-)

4、高匿代理(Elite proxy或High Anonymity Proxy)

REMOTE_ADDR = Proxy IP

HTTP_VIA = not determined

HTTP_X_FORWARDED_FOR = not determined

可以看出來,高匿代理讓別人根本無法發現你是在用代理,所以是最好的選擇。

一般我們搞爬蟲 用的都是 高匿的代理IP;

那代理IP 從哪裏搞呢 很簡單 百度一下,你就知道 一大堆代理IP站點。 一般都會給出一些免費的,但是花點錢搞收費接口更加方便;

比如 http://www.66ip.cn/

httpClient使用代理IP代碼:

package httpclient;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
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.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception, IOException
    {
        CloseableHttpClient httpclient = HttpClients.createDefault(); //創建httpclient
        HttpGet httpGet = new HttpGet("https://www.taobao.com/"); //創建get 請求
        HttpHost proxy = new HttpHost("115.202.167.56",808);
        RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
        //設置代理IP
        httpGet.setConfig(config);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
        CloseableHttpResponse response = httpclient.execute(httpGet); //通過httpcleint 發送get 請求
        if(response != null)
        {
            //System.out.println(response.getStatusLine().getStatusCode());
            HttpEntity httpentity = response.getEntity(); //獲取響應
            if(httpentity != null)
            {
                System.out.println(httpentity.getContentType().getValue());
                System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
                //System.out.println(httpentity.getContentType().getValue()); //判斷內容的類型,因為我們要采集圖片,所以要過濾掉其它內容.
                //接著,圖片肯定要通過流的方式去讀取.
                //InputStream inputStream = httpentity.getContent();
                //然後通過輸入流,然後讀取流,輸出流,則可以轉換讀取我們的圖片,流的復制.
                //在這裏我們可以 通過Apache 提供的IO 工具流來直接進行流的復制.
                //FileUtils.copyToFile(inputStream, new File("D://a.jpg")); //實際中是要拷貝真是的目錄下面,並且圖片的名稱也是唯一的。
            }
            //System.out.println(httpentity.getContentType().getValue());
            //System.out.println(EntityUtils.toString(httpentity,"UTF-8")); //采用工具來將實體進行轉換輸出
        }
        response.close();
        httpclient.close();
    }
}

通過代理IP,測試結果如下:

技術分享

其它知識點:

如連接超時時間設置,讀取內容超時時間設置等。

實例三:連接超時和內容超時

httpClient在執行具體http請求時候 有一個連接的時間和讀取內容的時間;

HttpClient連接時間

所謂連接的時候 是HttpClient發送請求的地方開始到連接上目標url主機地址的時間,理論上是距離越短越快,

線路越通暢越快,但是由於路由復雜交錯,往往連接上的時間都不固定,運氣不好連不上,HttpClient的默認連接時間,據我測試,

默認是1分鐘,假如超過1分鐘 過一會繼續嘗試連接,這樣會有一個問題 假如遇到一個url老是連不上,會影響其他線程的線程進去,說難聽點,

就是蹲著茅坑不拉屎。所以我們有必要進行特殊設置,比如設置10秒鐘 假如10秒鐘沒有連接上 我們就報錯,這樣我們就可以進行業務上的處理,

比如我們業務上控制 過會再連接試試看。並且這個特殊url寫到log4j日誌裏去。方便管理員查看。

HttpClient讀取時間

所謂讀取的時間 是HttpClient已經連接到了目標服務器,然後進行內容數據的獲取,一般情況 讀取數據都是很快速的,

但是假如讀取的數據量大,或者是目標服務器本身的問題(比如讀取數據庫速度慢,並發量大等等..)也會影響讀取時間。

同上,我們還是需要來特殊設置下,比如設置10秒鐘 假如10秒鐘還沒讀取完,就報錯,同上,我們可以業務上處理。

HttpClient給我們提供了一個RequestConfig類 專門用於配置參數比如連接時間,讀取時間以及前面講解的代理IP等。

主要通過:

RequestConfig config=RequestConfig.custom()
                .setConnectTimeout(5000)
                .setSocketTimeout(5000)
                .build();
        httpGet.setConfig(config);

到此,httpclient 相關的一些基本知識就學到這裏了,如果深入學習,可以參考httpclient的書籍深入.

httpclient 學習