1.HttpClient簡介
http協議可以說是現在Internet上面最重要,使用最多的協議之一了,越來越多的java應用需要使用http協議來訪問網絡資源,特別是現在rest api的流行,HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient,很多的java的爬蟲也是通過HttpClient實現的,研究HttpClient對我們來說是非常重要的。
2.HttpClient不是瀏覽器
很多人覺得既然HttpClient是一個HTTP客戶端編程工具,很多人把他當做瀏覽器來理解,但是其實HttpClient不是瀏覽器,它是一個HTTP通信庫,因此它只提供一個通用瀏覽器應用程序所期望的功能子集,最根本的區別是HttpClient中沒有用戶界面,瀏覽器需要一個渲染引擎來顯示頁面,並解釋用戶輸入,例如鼠標點擊顯示頁面上的某處,有一個布局引擎,計算如何顯示HTML頁面,包括級聯樣式表和圖像。javascript解釋器運行嵌入HTML頁面或從HTML頁面引用的javascript代碼。來自用戶界面的事件被傳遞到javascript解釋器進行處理。除此之外,還有用於插件的接口,可以處理Applet,嵌入式媒體對象(如pdf文件,Quicktime電影和Flash動畫)或ActiveX控件(可以執行任何操作)。HttpClient只能以編程的方式通過其API用於傳輸和接受HTTP消息。HttpClient也是完全內容不可知的。
另一個主要區別是對錯誤輸入或HTTP標准違規的容忍。 需要允許無效的用戶輸入,以使瀏覽器用戶友好。 還需要對從服務器檢索的畸形文檔的容忍度,以及在執行協議時服務器行為的缺陷,使盡可能多的用戶可訪問的網站。 然而,HttpClient努力在默認情況下盡可能接近並遵守HTTP標准規范和相關標准。 它還提供了一些手段來放松規范所施加的一些限制,這些限制允許或要求與不兼容的HTTP源或代理服務器兼容。
3.HttpClient入門使用
注意這個版本主要是基於HttpClient4.5.2版本的來講解的,也是現在最新的版本,之所以要提供版本說明的是因為HttpClient 3版本和HttpClient 4版本差別還是很多大的,基本HttpClient里面的接口都變了,你把HttpClient 3版本的代碼拿到HttpClient 4上面都運行不起來,會報錯的。所以這兒一定要注意,好了廢話不多說了,開始。
3.1.在pom.xml加入對httpclient的必需的jar包的依賴
//httpclient的接口基本都在這兒 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> //httpclient緩存 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient-cache</artifactId> <version>4.5</version> </dependency> //http的mime類型都在這里面 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.3.2</version> </dependency>
注意:常見的MIME類型(通用型):
超文本標記語言文本 .html text/html
xml文檔 .xml text/xml
XHTML文檔 .xhtml application/xhtml+xml
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
PDF文檔 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG圖像 .png image/png
GIF圖形 .gif image/gif
JPEG圖形 .jpeg,.jpg image/jpeg
au聲音文件 .au audio/basic
MIDI音樂文件 mid,.midi audio/midi,audio/x-midi
RealAudio音樂文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
TAR文件 .tar application/x-tar
任意的二進制數據 application/octet-stream
3.2.抓取網頁的內容並打印到控制台的demo
先直接貼代碼:
package fangdd.HttpClientDemo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Locale; import org.apache.http.HttpEntity; import org.apache.http.client.HttpClient; 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; public class HttpGetNewSample { public static void main(String[] args) { // TODO Auto-generated method stub String url="http://www.baidu.com"; //1.使用默認的配置的httpclient CloseableHttpClient client = HttpClients.createDefault(); //2.使用get方法 HttpGet httpGet = new HttpGet(url); InputStream inputStream = null; CloseableHttpResponse response = null; try { //3.執行請求,獲取響應 response = client.execute(httpGet); //看請求是否成功,這兒打印的是http狀態碼 System.out.println(response.getStatusLine().getStatusCode()); //4.獲取響應的實體內容,就是我們所要抓取得網頁內容 HttpEntity entity = response.getEntity(); //5.將其打印到控制台上面 //方法一:使用EntityUtils if (entity != null) { System.out.println(EntityUtils.toString(entity, "utf-8")); } EntityUtils.consume(entity); //方法二 :使用inputStream /* if (entity != null) { inputStream = entity.getContent(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line = ""; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } }*/ } catch (UnsupportedOperationException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (response != null) { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
3.3HttpClient編寫程序流程總結
其實從上面我們就可以總結出使用HttpClient其實分為6個步驟
1.創建HttpClient對象
這兒使用的是org.apache.http.impl.client.CloseableHttpClient,他是HttpClient接口的一個實例,創建該對象的最簡單方法是CloseableHttpClient client = HttpClients.createDefault();
HttpClients是創建CloseableHttpClient的工廠,采用默認的配置來創建實例,一般情況下我們就用這個默認的實例就足夠,后面我們可以去看下怎么定制自己需求配置的來創建HttpClient接口的實例。如果你去看這個函數的源代碼,你可以看到org.apache.http.client.CookieStore,org.apache.http.client.config.RequestConfig等等都是采用默認的。后面我們會專門有篇博客探討怎么根據自己的需求定制httpclient。
2.創建某種請求方法的實例。
創建某種請求的實例,並指定請求的url,如果是get請求,創建對象HttpGet,如果是post 請求,創建對象HttpPost。類型的還有 HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, 還有 HttpOptions。分別對應HEAD、POST PUT、DELETE、TRACE、OPTIONS方法,每個方法是做什么的如下表:
方法 | 描述 | 是否包含主體 |
GET | 從服務器獲取一份文檔 | 否 |
HEAD | 只從服務器獲取文檔的首部 | 否 |
POST | 向服務器發送需要處理的數據 | 是 |
PUT | 將請求的主體部分存儲在服務器上 | 是 |
TRACE | 對可能經過代理服務器傳送到服務器上去的報文進行追蹤 | 否 |
OPTIONS | 決定可以在服務器上執行哪些方法 | 否 |
DELETE | 從服務器上刪除一份文檔 | 否 |
可以看得到在Http協議中,只有post方法和put方法的請求里面有實體
3.如果有請求參數的話,Get方法直接寫在url后面,例如
HttpGet httpget = new HttpGet( “http://www.google.com/search?hl=zh-CN&q=httpclient&btnG=Google+Search&aq=f&oq=”); 或者使用setParameter來設置參數 URI uri = new URIBuilder() .setScheme(“http”) .setHost(“www.google.com”) .setPath(“/ search”) .setParameter(“q”,“httpclient”) .setParameter(“btnG”,“Google搜索”) .setParameter(“aq”,“f”) .setParameter(“oq”,“”) 。建立(); HttpGet httpget = new HttpGet(uri); System.out.println(httpget.getURI()); stdout> http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq= post方法用setEntity(HttpEntity entity)方法來設置請求參數。 后面會詳細的探討Entity這個東西,這兒就不在贅敘。
4.發送請求。
調用CloseableHttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個CloseableHttpResponse對象。 CloseableHttpResponse response = client.execute(post);,很明顯CloseableHttpResponse就是用了處理返回數據的實體,通過它我們可以拿到返回的狀態碼、首部、實體等等我們需要的東西。
5.獲取請求結果。
調用CloseableHttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用CloseableHttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。
HttpEntity entity = response.getEntity(); //5.將其打印到顯示器上面 //方法一:使用EntityUtils /* if(entity!=null) { System.out.println(EntityUtils.toString(entity,"utf-8")); } EntityUtils.consume(entity) */ //方法二 InputStream inputStream = entity.getContent(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line = ""; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); }
通過CloseableHttpEntity的getEntity取得實體之后,有兩種處理結果的方法,
方法一:使用EntityUtils來處理。
該類是官方提供的一個處理實體的工具類,toSting方法將返回的實體轉換為字符串,但是官網不建議使用這個,除非響應實體從一個可信HTTP服務器發起和已知是有限長度的。
方法二:使用InputStream來讀取
因為httpEntity.getContent方法返回的就是InputStream類型。這種方法是官網推薦的方式,需要記得的是要自己釋放底層資源。
6.關閉連接,釋放資源。
如果是使用EntityUtils來處理實體的使用 EntityUtils.consume(entity)來釋放資源,可以看得到該函數源碼為:
public static void consume(final HttpEntity entity) throws IOException { if (entity == null) { return; } if (entity.isStreaming()) { final InputStream instream = entity.getContent(); if (instream != null) { instream.close(); } } }
其實還是通過關閉inputStream,然后最后我們再關閉CloseableHttpResponse就可以了
如果是使用InputStream來處理實體的,釋放代碼如下
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet(“http:// localhost /”); CloseableHttpResponse response = httpclient.execute(httpget); try{ HttpEntity entity = response.getEntity(); if(entity!= null){ InputStream instream = entity.getContent(); try{ //做一些有用的事情 } finally { intream.close(); }} }} } finally { response.close(); }}
關閉內容流和關閉響應之間的區別是:前者將嘗試通過消耗實體內容來保持底層連接活動,而后者立即關閉並丟棄連接