httpclient的一些学习心得 (转)

本文介绍了一个项目中使用Apache HttpClient进行网页抓取的经验总结,包括资源释放、SSL证书处理、HTTP状态码理解、重定向管理、模拟浏览器请求及POST请求编码等关键实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://wallimn.iteye.com/blog/540566 (转)

 

最近忙于一个项目,了解下httpclient,在这里总结出来,和大家一起学习分享,希望各位朋友提出宝贵的意见。

首先介绍一下项目的背景:
  目标:把国内一家保险公司的“WEB一账通”改成“WAP一账通”。
  资源:客户不提供任何的webservice接口。
 
本项目中用到的第三方组件是apache的httpclient,一个非常强大的网页抓取工具(抓这个字用得可能不太好), 这里和大家
一起讨论下httpclient的一些常用用法和要注意的地方。

本文引用的资源列表:

  httpclient入门:  http://www.ibm.com/developerworks/cn/opensource/os-httpclient/
  httpclient证书导入:http://www.blogjava.net/happytian/archive/2006/12/22/89447.html
  httpclient高级认识:http://laohuang.iteye.com/blog/55613
  httpclient官方文档:http://hc.apache.org/httpcomponents-client/index.html
  httpclient资源关闭:http://www.iteye.com/topic/234759
 
 
上面的文章写得很好,看完之后也就知道怎么用httpclient这个很好的工具了,但是在这里还是补充一些比较重要的东西,也是项目中经
常碰到的问题。

首先要注意的有以下几点:
1、httpclient连接后资源释放问题很重要,就跟我们用database connection要释放资源一样。
2、https网站采用ssl加密传输,证书导入要注意。
3、做这样的项目最好先了解下http协义,比如302,301,200,404返回代码的含义(这是最基本的),cookie,session的机制。
4、httpclient的redirect状态默认是自动的,这在很大程度上给开发者很大的方便(如一些授权获得cookie),但是有时要手动管理下,比如
  有时会遇到CircularRedirectException异常,出现这样的情况是因为返回的头文件中location值指向之前重复(端口号可以不同)地址,导致可能会出现死
  循环递归重定向,这时可以手动关闭:method.setFollowRedirects(false)
5、有的网站会先判别用户的请求是否是来自浏览器,如不是,则返回不正确的文本,所以用httpclient抓取信息时在头部加入如下信息:
  header.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 1.7; .NET CLR 1.1.4322; CIBA; .NET CLR 2.0.50727)");
6、当post请求提交数据时要改变默认编码,不然的话提交上去的数据会出现乱码。重写postMethod的setContentCharSet()方法就可以了:





 
下面写一个通用类来处理request请求返回的文本:

Java代码 复制代码
  1. /*  
  2.  * HttpRequestProxy.java  
  3.  *  
  4.  * Created on November 3, 2008, 9:53 AM  
  5.  */  
  6.   
  7. package cn.com.mozat.net;   
  8.   
  9. import java.io.BufferedReader;   
  10. import java.io.IOException;   
  11. import java.io.InputStream;   
  12. import java.io.InputStreamReader;   
  13. import java.util.HashMap;   
  14. import java.util.Iterator;   
  15. import java.util.Map;   
  16. import java.util.Set;   
  17.   
  18. import org.apache.commons.httpclient.Header;   
  19. import org.apache.commons.httpclient.HttpClient;   
  20. import org.apache.commons.httpclient.HttpException;   
  21. import org.apache.commons.httpclient.HttpMethod;   
  22. import org.apache.commons.httpclient.NameValuePair;   
  23. import org.apache.commons.httpclient.SimpleHttpConnectionManager;   
  24. import org.apache.commons.httpclient.methods.GetMethod;   
  25. import org.apache.commons.httpclient.methods.PostMethod;   
  26.   
  27. import cn.com.mozat.exception.CustomException;   
  28.   
  29. /**  
  30.  *   
  31.  * @author bird  email:lihongfu-84@163.com  
  32.  *  
  33.  * 2008-11-4  09:49:48  
  34.  */  
  35. public class HttpRequestProxy{   
  36.     //超时间隔   
  37.     private static int connectTimeOut = 60000;   
  38.  //让connectionmanager管理httpclientconnection时是否关闭连接   
  39.     private static boolean alwaysClose = false;   
  40.  //返回数据编码格式   
  41.     private String encoding = "UTF-8";   
  42.        
  43.     private final HttpClient client = new HttpClient(new SimpleHttpConnectionManager(alwaysClose));   
  44.     
  45.     public HttpClient getHttpClient(){   
  46.         return client;   
  47.     }   
  48.          
  49.     /**  
  50.      * 用法:  
  51.      * HttpRequestProxy hrp = new HttpRequestProxy();  
  52.      * hrp.doRequest("http://www.163.com",null,null,"gbk");  
  53.      *   
  54.      * @param url  请求的资源URL  
  55.      * @param postData  POST请求时form表单封装的数据 没有时传null  
  56.      * @param header   request请求时附带的头信息(header) 没有时传null  
  57.      * @param encoding response返回的信息编码格式 没有时传null  
  58.      * @return  response返回的文本数据  
  59.      * @throws CustomException   
  60.      */  
  61.     public String doRequest(String url,Map postData,Map header,String encoding) throws CustomException{   
  62.      String responseString = null;   
  63.      //头部请求信息   
  64.      Header[] headers = null;   
  65.      if(header != null){   
  66.       Set entrySet = header.entrySet();   
  67.          int dataLength = entrySet.size();   
  68.           headers= new Header[dataLength];   
  69.          int i = 0;   
  70.          for(Iterator itor = entrySet.iterator();itor.hasNext();){   
  71.           Map.Entry entry = (Map.Entry)itor.next();   
  72.           headers[i++] = new Header(entry.getKey().toString(),entry.getValue().toString());   
  73.          }   
  74.      }   
  75.      //post方式   
  76.         if(postData!=null){   
  77.          PostMethod postRequest = new PostMethod(url.trim());   
  78.          if(headers != null){   
  79.           for(int i = 0;i < headers.length;i++){   
  80.            postRequest.setRequestHeader(headers[i]);   
  81.           }   
  82.          }   
  83.          Set entrySet = postData.entrySet();   
  84.          int dataLength = entrySet.size();   
  85.          NameValuePair[] params = new NameValuePair[dataLength];   
  86.          int i = 0;   
  87.          for(Iterator itor = entrySet.iterator();itor.hasNext();){   
  88.           Map.Entry entry = (Map.Entry)itor.next();   
  89.           params[i++] = new NameValuePair(entry.getKey().toString(),entry.getValue().toString());   
  90.          }   
  91.          postRequest.setRequestBody(params);   
  92.          try {   
  93.     responseString = this.executeMethod(postRequest,encoding);   
  94.    } catch (CustomException e) {   
  95.     throw e;   
  96.    } finally{   
  97.     postRequest.releaseConnection();   
  98.    }   
  99.         }   
  100.       //get方式   
  101.         if(postData == null){   
  102.          GetMethod getRequest = new GetMethod(url.trim());   
  103.          if(headers != null){   
  104.           for(int i = 0;i < headers.length;i++){   
  105.            getRequest.setRequestHeader(headers[i]);   
  106.           }   
  107.          }   
  108.          try {   
  109.     responseString = this.executeMethod(getRequest,encoding);   
  110.    } catch (CustomException e) {   
  111.                 e.printStackTrace();   
  112.     throw e;   
  113.    }finally{   
  114.     getRequest.releaseConnection();   
  115.    }   
  116.         }   
  117.     
  118.         return responseString;   
  119.     }   
  120.   
  121.  private String executeMethod(HttpMethod request, String encoding) throws CustomException{   
  122.   String responseContent = null;   
  123.   InputStream responseStream = null;   
  124.   BufferedReader rd = null;   
  125.   try {   
  126.    this.getHttpClient().executeMethod(request);   
  127.    if(encoding != null){   
  128.     responseStream = request.getResponseBodyAsStream();   
  129.      rd = new BufferedReader(new InputStreamReader(responseStream,   
  130.                       encoding));   
  131.               String tempLine = rd.readLine();   
  132.               StringBuffer tempStr = new StringBuffer();   
  133.               String crlf=System.getProperty("line.separator");   
  134.               while (tempLine != null)   
  135.               {   
  136.                   tempStr.append(tempLine);   
  137.                   tempStr.append(crlf);   
  138.                   tempLine = rd.readLine();   
  139.               }   
  140.               responseContent = tempStr.toString();   
  141.    }else  
  142.     responseContent = request.getResponseBodyAsString();   
  143.               
  144.    Header locationHeader = request.getResponseHeader("location");   
  145.    //返回代码为302,301时,表示页面己经重定向,则重新请求location的url,这在   
  146.    //一些登录授权取cookie时很重要   
  147.    if (locationHeader != null) {   
  148.              String redirectUrl = locationHeader.getValue();   
  149.              this.doRequest(redirectUrl, nullnull,null);   
  150.          }   
  151.   } catch (HttpException e) {   
  152.    throw new CustomException(e.getMessage());   
  153.   } catch (IOException e) {   
  154.    throw new CustomException(e.getMessage());   
  155.   
  156.   } finally{   
  157.    if(rd != null)   
  158.     try {   
  159.      rd.close();   
  160.     } catch (IOException e) {   
  161.      throw new CustomException(e.getMessage());   
  162.     }   
  163.     if(responseStream != null)   
  164.      try {   
  165.       responseStream.close();   
  166.      } catch (IOException e) {   
  167.       throw new CustomException(e.getMessage());   
  168.   
  169.      }   
  170.   }   
  171.   return responseContent;   
  172.  }   
  173.     
  174.       
  175.  /**  
  176.   * 特殊请求数据,这样的请求往往会出现redirect本身而出现递归死循环重定向  
  177.   * 所以单独写成一个请求方法  
  178.   * 比如现在请求的url为:http://localhost:8080/demo/index.jsp  
  179.   * 返回代码为302 头部信息中location值为:http://localhost:8083/demo/index.jsp  
  180.   * 这时httpclient认为进入递归死循环重定向,抛出CircularRedirectException异常  
  181.   * @param url  
  182.   * @return  
  183.   * @throws CustomException   
  184.   */  
  185.  public String doSpecialRequest(String url,int count,String encoding) throws CustomException{   
  186.   String str = null;   
  187.   InputStream responseStream = null;   
  188.   BufferedReader rd = null;   
  189.   GetMethod getRequest = new GetMethod(url);   
  190.   //关闭httpclient自动重定向动能   
  191.   getRequest.setFollowRedirects(false);   
  192.   try {   
  193.       
  194.    this.client.executeMethod(getRequest);   
  195.    Header header = getRequest.getResponseHeader("location");   
  196.    if(header!= null){   
  197.     //请求重定向后的URL,count同时加1   
  198.     this.doSpecialRequest(header.getValue(),count+1, encoding);   
  199.    }   
  200.    //这里用count作为标志位,当count为0时才返回请求的URL文本,   
  201.    //这样就可以忽略所有的递归重定向时返回文本流操作,提高性能   
  202.    if(count == 0){   
  203.     getRequest = new GetMethod(url);   
  204.     getRequest.setFollowRedirects(false);   
  205.     this.client.executeMethod(getRequest);   
  206.     responseStream = getRequest.getResponseBodyAsStream();   
  207.     rd = new BufferedReader(new InputStreamReader(responseStream,   
  208.                       encoding));   
  209.              String tempLine = rd.readLine();   
  210.              StringBuffer tempStr = new StringBuffer();   
  211.              String crlf=System.getProperty("line.separator");   
  212.              while (tempLine != null)   
  213.              {   
  214.                  tempStr.append(tempLine);   
  215.                  tempStr.append(crlf);   
  216.                  tempLine = rd.readLine();   
  217.              }   
  218.              str = tempStr.toString();   
  219.    }   
  220.       
  221.   } catch (HttpException e) {   
  222.    throw new CustomException(e.getMessage());   
  223.   } catch (IOException e) {   
  224.    throw new CustomException(e.getMessage());   
  225.   } finally{   
  226.    getRequest.releaseConnection();   
  227.    if(rd !=null)   
  228.     try {   
  229.      rd.close();   
  230.     } catch (IOException e) {   
  231.      throw new CustomException(e.getMessage());   
  232.     }   
  233.     if(responseStream !=null)   
  234.      try {   
  235.       responseStream.close();   
  236.      } catch (IOException e) {   
  237.       throw new CustomException(e.getMessage());   
  238.      }   
  239.   }   
  240.   return str;   
  241.  }   
  242.     
  243.     
  244.     
  245.     
  246.  public static void main(String[] args) throws Exception{   
  247.   HttpRequestProxy hrp = new HttpRequestProxy();   
  248.    Map header = new HashMap();   
  249.          header.put("User-Agent""Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 1.7; .NET CLR 1.1.4322; CIBA; .NET CLR 2.0.50727)");   
  250.   String str = hrp.doRequest(   
  251.     "http://www.cma-cgm.com/en/eBusiness/Tracking/Default.aspx?BolNumber=GZ2108827",   
  252.      null, header,null);   
  253.   System.out.println(str.contains("row_CRXU1587647"));   
  254. //  System.out.println(str);   
  255.  }   
  256.       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值