HttpClient對URL編碼的處理方式解惑!

 HttpClient是Apache基金下jakarta commons項目中的一個小項目,該項目封裝了對遠程地址下載的一些功能,最新版本爲3.0。該項目地址:http://jakarta.apache.org/commons/httpclient

最近在編寫Spider的時候就用到了HttpClient。在使用過程中發現一個有趣現象:有些URL的編碼方式是utf-8,有些URL的編碼方式是gbk。他總能夠正確識別,但是有些他又不能識別(抓取回來後是亂碼)。調用的是:httpMethod.getResponseBodyAsString(); 方法。

在進行進一步分析時,發現他對在http頭信息中有charset描述的就正確正常識別。如:

HTTP/1.1 200 OK   
Connection: close   
Content-Type: text/html; charset=utf-8  
Set-Cookie: _session_id=066875c3c0530c06c0204b96db403560; domain=javaeye.com; path=/   
Vary: Accept-Encoding   
Cache-Control: no-cache   
Content-Encoding: gzip   
Content-Length: 8512  
Date: Fri, 16 Mar 2007 09:02:52 GMT   
Server: lighttpd/1.4.13  

而沒有charset描述信息時,就會是亂碼。再查看相關文檔時,可以指定URL的編碼方式。如:HttpClientParams.setContentCharset("gbk");,指定了編碼後,就能夠正確識別對應編碼的URL了。問題出現了,因URL編碼不一樣,Spider不可能把URL的編碼方式寫死。並且只有在抓取回來後才知道編碼是否正確。於是再仔細研究一下httpclient的源代碼,發現他使用編碼的順序是:http頭信息的charset,如果頭信息中沒有charset,則查找HttpClientParams的contentCharset,如果沒有指定編碼,則是ISO-8859-1。

 

  1. /**    
  2.    * Returns the character set from the Content-Type header.    
  3.    *     
  4.    * @param contentheader The content header.    
  5.    * @return String The character set.    
  6.    */    
  7. protected String getContentCharSet(Header contentheader) {     
  8.       LOG.trace("enter getContentCharSet( Header contentheader )");     
  9.       String charset = null;     
  10.       if (contentheader != null) {     
  11.           HeaderElement values[] = contentheader.getElements();     
  12.           // I expect only one header element to be there     
  13.           // No more. no less     
  14.           if (values.length == 1) {     
  15.               NameValuePair param = values[0].getParameterByName("charset");     
  16.               if (param != null) {     
  17.                   // If I get anything "funny"      
  18.                   // UnsupportedEncondingException will result     
  19.                   charset = param.getValue();     
  20.               }     
  21.           }     
  22.       }     
  23.       if (charset == null) {     
  24.           charset = getParams().getContentCharset();     
  25.           if (LOG.isDebugEnabled()) {     
  26.               LOG.debug("Default charset used: " + charset);     
  27.           }     
  28.       }     
  29.       return charset;     
  30. }     
  31.     
  32.     
  33. /**      
  34. * Returns the default charset to be used for writing content body,       
  35. * when no charset explicitly specified.      
  36. * @return The charset      
  37. */       
  38. public String getContentCharset() {        
  39.      String charset = (String) getParameter(HTTP_CONTENT_CHARSET);        
  40.      if (charset == null) {        
  41.          LOG.warn("Default content charset not configured, using ISO-8859-1");        
  42.          charset = "ISO-8859-1";        
  43.      }        
  44.      return charset;        
  45. }       

這個該死的iso-8859-1害了多少人啊(Tomcat對提交的數據處理默認也是iso-8859-1)!!

經過仔細思考後,決定httpclient再封裝一次,思路如下:

先不設定HttpClientParams的charset;
executemethod後,再檢查http頭信息中的charset是否存在;
如果charset存在,返回httpMethod.getResponseBodyAsString(); ;
如果charset不存在,則先調用httpMethod.getResponseBodyAsString();得到html後,再分析html head的meta的charset <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">;
從meta中分析出charset後,設置到HttpClientParams的contentCharset;
再調用httpMethod.getResponseBodyAsString(),並返回該值。
經過以上思路處理後,發現抓回來的URL再也沒有亂碼了。爽!

以上步驟中,就是第四步稍微麻煩一些,不過,也可以利用第三方的html paser工具來分析meta的charset!

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