httpclient連接池的使用

httpclient是apache的一個項目:http://hc.apache.org/

文檔比較完善:http://hc.apache.org/httpcomponents-client-ga/tutorial/html/

這裏就不囉嗦了,主要是在做demo的時候遇到的一些問題在這裏總結一下:

[引用請註明出處http://blog.csdn.net/bhq2010/article/details/9210007]

1、使用連接池

雖說http協議時無連接的,但畢竟是基於tcp的,底層還是需要和服務器建立連接的。對於需要從同一個站點抓取大量網頁的程序,應該使用連接池,否則每次抓取都和Web站點建立連接、發送請求、獲得響應、釋放連接,一方面效率不高,另一方面稍不小心就會疏忽了某些資源的釋放、導致站點拒絕連接(很多站點會拒絕同一個ip的大量連接、防止DOS攻擊)。

連接池的例程如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. SchemeRegistry schemeRegistry = new SchemeRegistry();  
  2.     schemeRegistry.register(new Scheme("http"80, PlainSocketFactory.getSocketFactory()));  
  3. schemeRegistry.register(new Scheme("https"443, SSLSocketFactory.getSocketFactory()));  
  4. PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);  
  5.     cm.setMaxTotal(200);  
  6.     cm.setDefaultMaxPerRoute(2);  
  7.     HttpHost googleResearch = new HttpHost("research.google.com"80);  
  8.     HttpHost wikipediaEn = new HttpHost("en.wikipedia.org"80);  
  9.     cm.setMaxPerRoute(new HttpRoute(googleResearch), 30);  
  10.     cm.setMaxPerRoute(new HttpRoute(wikipediaEn), 50);  

SchemaRegistry的作用是註冊協議的默認端口號。PoolingClientConnectionManager是池化連接管理器,即連接池,setMaxTotal設置連接池的最大連接數,setDefaultMaxPerRoute設置每個路由(http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e467)上的默認連接個數,setMaxPerRoute則單獨爲某個站點設置最大連接個數。

從連接池中獲取http client也很方面:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. DefaultHttpClient client = new DefaultHttpClient(cm);  

2、設置HttpClient參數

HttpClient需要設置合適的參數,才能更好地工作。默認的參數能夠應付少量的抓取工作,但找到一組合適的參數往往能改善特定情況下的抓取效果。設置參數的例程如下:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. DefaultHttpClient client = new DefaultHttpClient(cm);  
  2. Integer socketTimeout = 10000;  
  3. Integer connectionTimeout = 10000;  
  4. final int retryTime = 3;  
  5. client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, socketTimeout);  
  6. client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);  
  7. client.getParams().setParameter(CoreConnectionPNames.TCP_NODELAY, false);  
  8. client.getParams().setParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 1024 * 1024);  
  9. HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler()  
  10. {  
  11.     @Override  
  12.     public boolean retryRequest(IOException exception, int executionCount, HttpContext context)  
  13.     {  
  14.         if (executionCount >= retryTime)  
  15.         {  
  16.             // Do not retry if over max retry count  
  17.             return false;  
  18.         }  
  19.         if (exception instanceof InterruptedIOException)  
  20.         {  
  21.             // Timeout  
  22.             return false;  
  23.         }  
  24.         if (exception instanceof UnknownHostException)  
  25.         {  
  26.             // Unknown host  
  27.             return false;  
  28.         }  
  29.         if (exception instanceof ConnectException)  
  30.         {  
  31.             // Connection refused  
  32.             return false;  
  33.         }  
  34.         if (exception instanceof SSLException)  
  35.         {  
  36.             // SSL handshake exception  
  37.             return false;  
  38.         }  
  39.         HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);  
  40.         boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);  
  41.         if (idempotent)  
  42.         {  
  43.             // Retry if the request is considered idempotent  
  44.             return true;  
  45.         }  
  46.         return false;  
  47.     }  
  48.   
  49. };  
  50. client.setHttpRequestRetryHandler(myRetryHandler);  

5、6行分別設置了Socket最大等待時間、連接最大等待時間(單位都是毫秒)。socket等待時間是指從站點下載頁面和數據時,兩個數據包之間的最大時間間隔,超過這個時間間隔,httpclient就認爲連接出了故障。連接最大等待時間則是指和站點建立連接時的最大等待時間,超過這個時間站點不給迴應,則認爲站點無法連接。第7行設置httpclient不使用NoDelay策略。如果啓用了NoDelay策略,httpclient和站點之間傳輸數據時將會儘可能及時地將發送緩衝區中的數據發送出去、而不考慮網絡帶寬的利用率,這個策略適合對實時性要求高的場景。而禁用了這個策略之後,數據傳輸會採用Nagle's algorithm發送數據,該算法會充分顧及帶寬的利用率,而不是數據傳輸的實時性。第8行設置socket緩衝區的大小(單位爲字節),默認是8KB。
HttpRequestRetryHandler是負責處理請求重試的接口。在該接口的內部類中實現RetryRequest方法即可。當httpclient發送請求之後出現異常時,就會調用這個方法。在該方法中根據已執行請求的次數、請求內容、異常信息判斷是否繼續重試,若繼續重試返回true,否則返回false。

3、設置request header

設置request header也是很重要的,比如設置User-Agent可以將抓取程序僞裝成瀏覽器,騙過一些網站對爬蟲的檢查,設置Accept-Encoding爲gzip可以建議站點以壓縮格式傳輸數據、節省帶寬等等。例程如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. HttpResponse response = null;  
  2. HttpGet get = new HttpGet(url);  
  3. get.addHeader("Accept""text/html");  
  4. get.addHeader("Accept-Charset""utf-8");  
  5. get.addHeader("Accept-Encoding""gzip");  
  6. get.addHeader("Accept-Language""en-US,en");  
  7. get.addHeader("User-Agent""Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.160 Safari/537.22");  
  8. response = client.execute(get);  
  9. HttpEntity entity = response.getEntity();  
  10. Header header = entity.getContentEncoding();  
  11. if (header != null)  
  12. {  
  13.     HeaderElement[] codecs = header.getElements();  
  14.     for (int i = 0; i < codecs.length; i++)  
  15.     {  
  16.         if (codecs[i].getName().equalsIgnoreCase("gzip"))  
  17.         {  
  18.             response.setEntity(new GzipDecompressingEntity(entity));  
  19.         }  
  20.     }  
  21. }  
  22. return response;  

各個header的含義參考http://kb.cnblogs.com/page/92320/
需要的都設上就好了。如果需要很多不同的User-Agent輪流使用(同一個User-Agent對一個站點頻繁訪問容易被識別爲爬蟲而杯具),可以去網上找,也可以在自己的chrome瀏覽器裏看或者用抓包軟件抓。值得注意的是設置了Accept-Encoding爲gzip之後,對站點回復的內容要檢查是否是壓縮格式的,如果是,則解壓縮,如上面例程中第9行之後的代碼所示。
發佈了68 篇原創文章 · 獲贊 28 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章