HttpClient 学习整理

<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} -->

最近在做网页抓取的东西,他们都说用HttpClient , 所以在网上整理了一些东西,写得很好,寄放在这里。忘记文章是谁写的了(^_^)

HTTP
协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus HTMLUnit 都使用了 HttpClient ,更多使用 HttpClient 的应用可以参见http://wiki.apache.org/jakarta-httpclient/HttpClientPowered HttpClient 项目非常活跃,使用的人还是非常多的。目前 HttpClient 版本是在 2005.10.11 发布的 3.0 RC4

------------------------------------

应用HttpClient 来对付各种顽固的WEB 服务器
转自:http://blog.csdn.net/ambitiontan/archive/2006/01/06/572171.aspx

一 般的情况下我们都是使用IE 或者Navigator 浏览器来访问一个WEB 服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅 是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS 。目前我们使用的浏览器处理这些情况都不会构成 问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中 一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想 知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页 面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授 权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL 就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到 COOKIE 问题的处理。我们知道目前流行的动态网页技术例如ASPJSP 无不是通过COOKIE 来处理会话信息的。为了使我们的程序能使用别人所提供 的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie ,想想当你用java.net.HttpURLConnection 来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB 服务器中的一个很常见的 顽固 !再有如通过HTTP 来上传文件呢?不需要头 疼,这些问题有了 就很容易解决了!

我们不可能列举所有可能的顽固,我们会针对几种最常见 的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection 来搞定这些问题是很恐怖的事情,因此在 开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache 开源组织中的httpclient ,它隶属于Jakartacommons 项 目,目前的版本是2.0RC2commons 下本来已经有一个net 的子项目,但是又把httpclient 单独提出来,可见http 服务器的访问绝非 易事。

Commons-httpclient 项目就是专门设计来简化HTTP 客户端与服务器进行各种通讯编程。通过它可以让原来很 头疼的事情现在轻松的解决,例如你不再管是HTTP 或者HTTPS 的通讯方式,告诉它你想使用HTTPS 方式,剩下的事情交给httpclient 替你完 成。本文会针对我们在编写HTTP 客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient 来解决它们,为了让读者更快的熟悉这个项目我 们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所有问题。

1   读取网页(HTTP/HTTPS) 内容

下面是我们给出的一个简单的例子用来访问某个页面

/*
* Created on 2003-12-14 by Liudong
*/

package http.demo;
import
java.io.IOException;
import
org.apache.commons.httpclient.*;
import
org.apache.commons.httpclient.methods.*;

/**  
 * 最简单的 HTTP 客户端 , 用来演示通过 GET 或者 POST 方式访问某个页面
 
* @author Liudong
*/

public class SimpleClient {
public static void main(String[] args) throws IOException
{
  HttpClient client = new HttpClient();
      //
设置代理服务器地址和端口      

      //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);  
      //
使用 GET 方法 ,如果服务器需要通过 HTTPS 连接,那只需要将下面 URL 中的 http 换成 https
        
HttpMethodmethod= new GetMethod( "http://java.sun.com" );
      // 使用 POST 方法
      //HttpMethod method = new PostMethod("http://java.sun.com");
      
client.executeMethod(method);

      
// 打印服务器返回的状态
      
System.out.println(method.getStatusLine());
      
// 打印返回的信息
      
System.out.println(method.getResponseBodyAsString());
      
// 释放连接
      
method.releaseConnection();
   }
}

 

这个例子中首先创建一个 HTTP 客户端 (HttpClient) 的实例,然后选择提交的方法是 GET 或者 POST ,最后在 HttpClient 实例上执行 提交的方法,最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用 HttpClient 的基本流程。其实用一行代码也就可以搞定整个请求的过 程,非常的简单!


2
 GET 或者POST 方式向网页提交参数

其 实前面一个最简单的示例中我们已经介绍了如何使用GET 或者POST 方式来请求一个页面,本小节与之不同的是多了提交时设定页面所需的参数,我们知道如果 是GET 的请求方式,那么所有参数都直接放到页面的URL 后面用问号与页面地址隔开,每个参数用& 隔开,例如:http://java.sun.com/?name=liudong&mobile=123456 ,但是当使用POST 方法时就会稍微有一点点麻烦。本小节的例子演示向如何查询手机号码所在的城市,代码如下:

 

/*
* Created on 2003-12-7 by Liudong  
*/

package http.demo;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

/**  
  * 提交参数演示
 * 该程序连接到一个用于查询手机号码所属地的页面
 * 以便查询号码段 1330227 所在的省份以及城市
 * @author Liudong
 */

public class SimpleHttpClient { 
   public static void main(String[] args) throws IOException {
      HttpClient client =
new HttpClient();
      client.getHostConfiguration().setHost(
"www.imobile.com.cn" , 80, "http" );
      method = getPostMethod(); 
   // 使用 POST 方式提交数据  
      client.executeMethod(method);    // 打印服务器返回的状态  
      System.out.println(method.getStatusLine());    // 打印结果页面
      Stringresponse= new String(method.getResponseBodyAsString().getBytes( "8859_1" ));

      // 打印返回的信息
      System.out.println(response);
      method.releaseConnection();
   }

   /**  
    * 使用 GET 方式提交数据  
    * @return  
    */

   privatestatic HttpMethodgetGetMethod(){
      returnnew GetMethod( "/simcard.php?simcard=1330227" );
   }

    /**  
     * 使用 POST 方式提交数据  
     * @return  
     */

    private static HttpMethod getPostMethod(){
      PostMethod post =
new PostMethod( "/simcard.php" );
      NameValuePair simcard =
new NameValuePair( "simcard" , "1330227" );
      post.setRequestBody(
new NameValuePair[] { simcard});
      return post;
   }
}

在上面的例子中页面 http://www.imobile.com.cn/simcard.php 要一个参数是 simcard ,这个参数值为手机号码段,即手机号码的前七位,服务器会返回提交的手机号码对应的省份、城市以及其他详细信息。 GET 的提交 方法只需要在 URL 后加入参数信息,而 POST 则需要通过 NameValuePair 类来设置参数名称和它所对应的值。

3
  处理页面重定向

JSP/Servlet 编程中 response.sendRedirect 方法就是使用 HTTP 协议中的重定向机制。它与 JSP <jsp:forward …> 的区别在于后者是在服务器中实现页面的跳转,也就是说应用容器加载了所要跳转的页面的内容并返回给客户端;而前者是返回一个状态码,这些状态码 的可能值见下表,然后客户端读取需要跳转到的页面的 URL 并重新加载新的页面。就是这样一个过程,所以我们编程的时候就要通过 HttpMethod.getStatusCode() 方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可 以通过读取 HTTP 头中的 location 属性来获取新的地址。

状态码

对应 HttpServletResponse 的常量

详细描述

301

SC_MOVED_PERMANENTLY

页面已经永久移到另外一个新地址

302

SC_MOVED_TEMPORARILY

页面暂时移动到另外一个新的地址

303

SC_SEE_OTHER

客户端请求的地址必须通过另外的 URL 来访问

307

SC_TEMPORARY_REDIRECT

SC_MOVED_TEMPORARILY

下面的代码片段演示如何处理页面的重定向

client.executeMethod(post);
System.out.println(post.getStatusLine().toString());
post.releaseConnection();
// 检查是否重定向
int statuscode = post.getStatusCode();
if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode == HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
// 读取新的 URL 地址  
   Headerheader=post.getResponseHeader( "location" );
   if (header!= null ){
      Stringnewuri=header.getValue();
      if ((newuri== null )||(newuri.equals( "" )))
         newuri=
"/" ;
         GetMethodredirect=
new GetMethod(newuri);
         client.executeMethod(redirect);
         System.out.println(
"Redirect:" +redirect.getStatusLine().toString());
         redirect.releaseConnection();
   }
else  
    System.out.println( "Invalid redirect" );
}

我们可以自行编写两个 JSP 页面,其中一个页面用 response.sendRedirect 方法重定向到另外一个页面用来测试上面的例子。

4   模拟输入用户名和口令进行登录

小节应该说是 HTTP 客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可 浏览到想要的页面。因为 HTTP 协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必 须使用到 Cookie 机制。以 JSP/Servlet 为例,当浏览器请求一个 JSP 或者是 Servlet 的页面时,应用服务器会返回一个参数,名为 jsessionid (因不同应用服务器而异),值是一个较长的唯一字符串的 Cookie ,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问 该站点的其他页面时候都要带上 jsessionid 这样的 Cookie 信息,应用服务器根据读取这个会话标识来获取对应的会话信息。

于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的 Cookie 读取当前请求对应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户 输入帐号和口令进行登录。这就是一般使用 JSP 开发网站在处理用户登录的比较通用的方法。

这样一来,对于 HTTP 的客户端 来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取 Cookie 值;再次请求登录页面并加入登录页所需的每 个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上 Cookie 信息以便服务器能判断当前请求是否已经通过验证。说了这么 多,可是如果你使用 httpclient 的话,你甚至连一行代码都无需增加,你只需要先传递登录信息执行登录过程,然后直接访问想要的页面,跟访问一个普 通的页面没有任何区别,因为类 HttpClient 已经帮你做了所有该做的事情了,太棒了!下面的例子实现了这样一个访问的过程。

/*
* Created on 2003-12-7 by Liudong
*/

package http.demo;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.cookie.*;
import org.apache.commons.httpclient.methods.*;

/**
 * 用来演示登录表单的示例
 * @author Liudong
 */

public class FormLoginDemo {
   static final String LOGON_SITE = "localhost" ;
   static final int     LOGON_PORT = 8080;

   public static void main(String[] args) throws Exception{
      HttpClient client =
new HttpClient();
      client.getHostConfiguration().setHost(LOGON_SITE, LOGON_PORT);

      // 模拟登录页面 login.jsp->main.jsp
      PostMethod post = new PostMethod( "/main.jsp" );
      NameValuePair name =
new NameValuePair( "name" , "ld" );
      NameValuePair pass =
new NameValuePair( "password" , "ld" );
      post.setRequestBody(
new NameValuePair[]{name,pass});
      int status = client.executeMethod(post);
      System.out.println(post.getResponseBodyAsString());
      post.releaseConnection();

      // 查看 cookie 信息
      CookieSpec cookiespec = CookiePolicy.getDefaultSpec();
      Cookie[] cookies = cookiespec.match(LOGON_SITE, LOGON_PORT,
"/" , false , client.getState().getCookies());
      if (cookies.length == 0) {
         System.out.println(
"None" );
      }
else {
         for ( int i = 0; i < cookies.length; i++) {
            System.out.println(cookies[i].toString());
         }
      }

      // 访问所需的页面 main2.jsp  
      GetMethodget= new GetMethod( "/main2.jsp" );
      client.executeMethod(get);
      System.out.println(get.getResponseBodyAsString());
      get.releaseConnection();
   }
}

5   提交 XML 格式参数

提交 XML 格式的参数很简单,仅仅是一个提交时候的 ContentType 问题,下面的例子演示从文件文件中读取 XML 信息并提交给服务器的过程,该过程可以用来测试 Web 服务。

import java.io.File;
import java.io.FileInputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.PostMethod;

/**  
  * 用来演示提交 XML 格式数据的例子
*/

public class PostXMLClient {

   public
static void main(String[] args) throws Exception {
      File input =
new File(“test.xml”);
      PostMethod post =
new PostMethod(“http://localhost:8080/httpclient/xml.jsp”);

      // 设置请求的内容直接从文件中读取
      post.setRequestBody( new FileInputStream(input)); 
      if (input.length() < Integer.MAX_VALUE)
         post.setRequestContentLength(input.length());
      else
         post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);

      // 指定请求内容的类型
      post.setRequestHeader( "Content-type" , "text/xml; charset=GBK" );
      HttpClient httpclient = new HttpClient();
      int result = httpclient.executeMethod(post);
      System.out.println(
"Response status code: " + result);
      System.out.println(
"Response body: " );
      System.out.println(post.getResponseBodyAsString()); 
      post.releaseConnection(); 
   }
}

6   通过 HTTP 上传文件

httpclient 使用了单独的一个 HttpMethod 子类来处理文件的上传,这个类就是 MultipartPostMethod ,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面的代码片段演示如何使用这个类。

MultipartPostMethod filePost = new MultipartPostMethod(targetURL);
filePost.addParameter(
"fileName" , targetFilePath);
HttpClient
client = new HttpClient();

// 由于要上传的文件可能比较大 , 因此在此设置最大的连接超时时间
client.getHttpConnectionManager(). getParams().setConnectionTimeout(5000);
int status = client.executeMethod(filePost);

上面代码中, targetFilePath 即为要上传的文件所在的路径。

7   访问启用认证的页面

们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户 身份验证。这是 HTTP 的认证策略, httpclient 支持三种认证方式包括:基本、摘要以及 NTLM 认证。其中基本认证最简单、通用但也最不安全;摘 要认证是在 HTTP 1.1 中加入的认证方式,而 NTLM 则是微软公司定义的而不是通用的规范,最新版本的 NTLM 是比摘要认证还要安全的一种方式。

下面例子是从 httpclient CVS 服务器中下载的,它简单演示如何访问一个认证保护的页面:

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.GetMethod;

public class BasicAuthenticationExample { 

   public BasicAuthenticationExample() { 
   }

   public static void main(String[] args) throws Exception {
      HttpClient client =
new HttpClient();
      client.getState().setCredentials(
"www.verisign.com" , "realm" , new UsernamePasswordCredentials( "username" , "password" ) );

      GetMethod get =
new GetMethod( "https://www.verisign.com/products/index.html" );
      get.setDoAuthentication(
true );
      int status = client.executeMethod( get );
      System.out.println(status+
"/n" + get.getResponseBodyAsString());
      get.releaseConnection();
   }
}

8   多线程模式下使用 httpclient

线程同时访问 httpclient ,例如同时从一个站点上下载多个文件。对于同一个 HttpConnection 同一个时间只能有一个线程访问,为了保证 多线程工作环境下不产生冲突, httpclient 使用了一个多线程连接管理器的 类: MultiThreadedHttpConnectionManager ,要使用这个类很简单,只需要在构造 HttpClient 实例的时候传入即 可,代码如下:

MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();

HttpClient client = new HttpClient(connectionManager);

以后尽管访问 client 实例即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值