如何使用HttpClient下载网络资源(包括下载ssl认证的资源)

看了两篇不错的博客,这里总结一下:

第一篇博客(http://hackerzhou.me/2010/08/support-ssl-proxy-post-get-the-webclient-using-httpclient-4-0-1.html):

[java]  view plain copy
  1. import java.io.IOException;  
  2. import java.security.KeyManagementException;  
  3. import java.security.NoSuchAlgorithmException;  
  4. import java.security.cert.CertificateException;  
  5. import java.security.cert.X509Certificate;  
  6. import java.util.ArrayList;  
  7. import java.util.HashMap;  
  8. import java.util.List;  
  9. import java.util.Map;  
  10. import java.util.Map.Entry;  
  11. import java.util.regex.Matcher;  
  12. import java.util.regex.Pattern;  
  13.    
  14. import javax.net.ssl.SSLContext;  
  15. import javax.net.ssl.TrustManager;  
  16. import javax.net.ssl.X509TrustManager;  
  17.    
  18. import org.apache.http.Header;  
  19. import org.apache.http.HttpException;  
  20. import org.apache.http.HttpHost;  
  21. import org.apache.http.HttpRequest;  
  22. import org.apache.http.HttpResponse;  
  23. import org.apache.http.NameValuePair;  
  24. import org.apache.http.auth.AuthScope;  
  25. import org.apache.http.auth.UsernamePasswordCredentials;  
  26. import org.apache.http.client.entity.UrlEncodedFormEntity;  
  27. import org.apache.http.client.methods.HttpGet;  
  28. import org.apache.http.client.methods.HttpPost;  
  29. import org.apache.http.client.methods.HttpUriRequest;  
  30. import org.apache.http.client.params.ClientPNames;  
  31. import org.apache.http.client.params.CookiePolicy;  
  32. import org.apache.http.conn.routing.HttpRoute;  
  33. import org.apache.http.conn.routing.HttpRoutePlanner;  
  34. import org.apache.http.conn.scheme.Scheme;  
  35. import org.apache.http.conn.scheme.SchemeRegistry;  
  36. import org.apache.http.conn.ssl.SSLSocketFactory;  
  37. import org.apache.http.impl.client.DefaultHttpClient;  
  38. import org.apache.http.message.BasicNameValuePair;  
  39. import org.apache.http.params.CoreProtocolPNames;  
  40. import org.apache.http.params.HttpConnectionParams;  
  41. import org.apache.http.params.HttpParams;  
  42. import org.apache.http.protocol.HTTP;  
  43. import org.apache.http.protocol.HttpContext;  
  44. import org.apache.http.util.EntityUtils;  
  45.    
  46. public class WebClient {  
  47.     private DefaultHttpClient httpClient = new DefaultHttpClient();  
  48.     private String url;  
  49.     private HTTPMethod method;  
  50.     private byte[] content;  
  51.     private Map<String, String> headers = new HashMap<String, String>();  
  52.     private int responseCode;  
  53.     private List<NameValuePair> postParameter = new ArrayList<NameValuePair>();  
  54.    
  55.     private static final Pattern pageEncodingReg = Pattern.compile(  
  56.             "content-type.*charset=([^\">\\\\]+)", Pattern.CASE_INSENSITIVE);  
  57.     private static final Pattern headerEncodingReg = Pattern.compile(  
  58.             "charset=(.+)", Pattern.CASE_INSENSITIVE);  
  59.    
  60.     public static void main(String[] args) throws Exception {  
  61.         WebClient web = new WebClient("http://www.baidu.com/", HTTPMethod.GET);  
  62.         web.enableProxy("10.58.32.51"8080falsenullnull"127.0.0.1");  
  63.         System.out.println(web.getTextContent());  
  64.         System.out.println("------------------------------------------");  
  65.         web.setUrl("https://mail.google.com/mail/");  
  66.         System.out.println(web.getTextContent());  
  67.         System.out.println("------------------------------------------");  
  68.         web.setUrl("http://www.snee.com/xml/crud/posttest.cgi");  
  69.         web.setMethod(HTTPMethod.POST);  
  70.         web.addPostParameter("fname""ababab");  
  71.         web.addPostParameter("lname""cdcdcd");  
  72.         System.out.println(web.getTextContent());  
  73.         System.out.println("------------------------------------------");  
  74.     }  
  75.    
  76.     // Without proxy  
  77.     public WebClient(String url, HTTPMethod method) {  
  78.         this(url, method, falsenull0falsenullnullnull);  
  79.     }  
  80.    
  81.     // Proxy without auth  
  82.     public WebClient(String url, HTTPMethod method, String proxyHost,  
  83.             int proxyPort) {  
  84.         this(url, method, true, proxyHost, proxyPort, falsenullnullnull);  
  85.     }  
  86.    
  87.     // All in one settings  
  88.     public WebClient(String url, HTTPMethod method, boolean useProxy,  
  89.             String proxyHost, int proxyPort, boolean needAuth, String username,  
  90.             String password, String nonProxyReg) {  
  91.         setUrl(url);  
  92.         setMethod(method);  
  93.         if (useProxy) {  
  94.             enableProxy(proxyHost, proxyPort, needAuth, username, password,  
  95.                     nonProxyReg);  
  96.         }  
  97.     }  
  98.    
  99.     public void setMethod(HTTPMethod method) {  
  100.         this.method = method;  
  101.     }  
  102.    
  103.     public void setUrl(String url) {  
  104.         if (isStringEmpty(url)) {  
  105.             throw new RuntimeException("[Error] url is empty!");  
  106.         }  
  107.         this.url = url;  
  108.         headers.clear();  
  109.         responseCode = 0;  
  110.         postParameter.clear();  
  111.         content = null;  
  112.         if (url.startsWith("https://")) {  
  113.             enableSSL();  
  114.         } else {  
  115.             disableSSL();  
  116.         }  
  117.     }  
  118.    
  119.     public Map<String, String> getRequestHeaders() {  
  120.         return headers;  
  121.     }  
  122.    
  123.     public void addPostParameter(String name, String value) {  
  124.         this.postParameter.add(new BasicNameValuePair(name, value));  
  125.     }  
  126.    
  127.     public void setTimeout(int connectTimeout, int readTimeout) {  
  128.         HttpParams params = httpClient.getParams();  
  129.         HttpConnectionParams.setConnectionTimeout(params, connectTimeout);  
  130.         HttpConnectionParams.setSoTimeout(params, readTimeout);  
  131.     }  
  132.    
  133.     private void enableSSL() {  
  134.         try {  
  135.             SSLContext sslcontext = SSLContext.getInstance("TLS");  
  136.             sslcontext.init(nullnew TrustManager[] { truseAllManager }, null);  
  137.             SSLSocketFactory sf = new SSLSocketFactory(sslcontext);  
  138.             sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
  139.             Scheme https = new Scheme("https", sf, 443);  
  140.             httpClient.getConnectionManager().getSchemeRegistry()  
  141.                     .register(https);  
  142.         } catch (KeyManagementException e) {  
  143.             e.printStackTrace();  
  144.         } catch (NoSuchAlgorithmException e) {  
  145.             e.printStackTrace();  
  146.         }  
  147.     }  
  148.    
  149.     private void disableSSL() {  
  150.         SchemeRegistry reg = httpClient.getConnectionManager()  
  151.                 .getSchemeRegistry();  
  152.         if (reg.get("https") != null) {  
  153.             reg.unregister("https");  
  154.         }  
  155.     }  
  156.    
  157.     public void disableProxy() {  
  158.         httpClient.getCredentialsProvider().clear();  
  159.         httpClient.setRoutePlanner(null);  
  160.     }  
  161.    
  162.     public void enableProxy(final String proxyHost, final int proxyPort,  
  163.             boolean needAuth, String username, String password,  
  164.             final String nonProxyHostRegularExpression) {  
  165.         if (needAuth) {  
  166.             httpClient.getCredentialsProvider().setCredentials(  
  167.                     new AuthScope(proxyHost, proxyPort),  
  168.                     new UsernamePasswordCredentials(username, password));  
  169.         }  
  170.         // Simple proxy setting, can't handle non-proxy-host  
  171.         // httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,new  
  172.         // HttpHost(proxyHost, proxyPort));  
  173.         httpClient.setRoutePlanner(new HttpRoutePlanner() {  
  174.             @Override  
  175.             public HttpRoute determineRoute(HttpHost target,  
  176.                     HttpRequest request, HttpContext contenxt)  
  177.                     throws HttpException {  
  178.                 HttpRoute proxyRoute = new HttpRoute(target, null,  
  179.                         new HttpHost(proxyHost, proxyPort), "https"  
  180.                                 .equalsIgnoreCase(target.getSchemeName()));  
  181.                 if (nonProxyHostRegularExpression == null) {  
  182.                     return proxyRoute;  
  183.                 }  
  184.                 Pattern pattern = Pattern  
  185.                         .compile(nonProxyHostRegularExpression,  
  186.                                 Pattern.CASE_INSENSITIVE);  
  187.                 Matcher m = pattern.matcher(target.getHostName());  
  188.                 if (m.find()) {  
  189.                     return new HttpRoute(target, null, target, "https"  
  190.                             .equalsIgnoreCase(target.getSchemeName()));  
  191.                 } else {  
  192.                     return proxyRoute;  
  193.                 }  
  194.             }  
  195.         });  
  196.     }  
  197.    
  198.     private void fetch() throws IOException {  
  199.         if (url == null || method == null) {  
  200.             throw new RuntimeException(  
  201.                     "Fetch exception: URL and Method is null");  
  202.         }  
  203.         httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY,  
  204.                 CookiePolicy.BROWSER_COMPATIBILITY);  
  205.         HttpResponse response = null;  
  206.         HttpUriRequest req = null;  
  207.         if (method.equals(HTTPMethod.GET)) {  
  208.             req = new HttpGet(url);  
  209.         } else {  
  210.             req = new HttpPost(url);  
  211.             ((HttpPost) req).setEntity(new UrlEncodedFormEntity(  
  212.                     this.postParameter, HTTP.UTF_8));  
  213.         }  
  214.         for (Entry<String, String> e : headers.entrySet()) {  
  215.             req.addHeader(e.getKey(), e.getValue());  
  216.         }  
  217.    
  218.         //  
  219.         // Turn off "except" http header, some proxy server and web server do  
  220.         // not support it, may cause "417 Expectation Failed"  
  221.         //  
  222.         // HttpClient's doc says: 100-continue handshake should be used with  
  223.         // caution, as it may cause problems with HTTP servers and proxies that  
  224.         // do not support HTTP/1.1 protocol.  
  225.         //  
  226.         req.getParams().setBooleanParameter(  
  227.                 CoreProtocolPNames.USE_EXPECT_CONTINUE, false);  
  228.         response = httpClient.execute(req);  
  229.         Header[] header = response.getAllHeaders();  
  230.         headers.clear();  
  231.         for (Header h : header) {  
  232.             headers.put(h.getName(), h.getValue());  
  233.         }  
  234.         content = EntityUtils.toByteArray(response.getEntity());  
  235.         responseCode = response.getStatusLine().getStatusCode();  
  236.     }  
  237.    
  238.     private boolean isStringEmpty(String s) {  
  239.         return s == null || s.length() == 0;  
  240.     }  
  241.    
  242.     public int getResponseCode() throws IOException {  
  243.         if (responseCode == 0) {  
  244.             fetch();  
  245.         }  
  246.         return responseCode;  
  247.     }  
  248.    
  249.     public Map<String, String> getResponseHeaders() throws IOException {  
  250.         if (responseCode == 0) {  
  251.             fetch();  
  252.         }  
  253.         return headers;  
  254.     }  
  255.    
  256.     public byte[] getByteArrayContent() throws IOException {  
  257.         if (content == null) {  
  258.             fetch();  
  259.         }  
  260.         return content;  
  261.     }  
  262.    
  263.     public String getTextContent() throws IOException {  
  264.         if (content == null) {  
  265.             fetch();  
  266.         }  
  267.         if (content == null) {  
  268.             throw new RuntimeException("[Error] Can't fetch content!");  
  269.         }  
  270.         String headerContentType = null;  
  271.         if ((headerContentType = headers.get("Content-Type")) != null) {  
  272.             // use http header encoding  
  273.             Matcher m1 = headerEncodingReg.matcher(headerContentType);  
  274.             if (m1.find()) {  
  275.                 return new String(content, m1.group(1));  
  276.             }  
  277.         }  
  278.         // Use html's encoding  
  279.         String html = new String(content);  
  280.         Matcher m2 = pageEncodingReg.matcher(html);  
  281.         if (m2.find()) {  
  282.             html = new String(content, m2.group(1));  
  283.         }  
  284.         return html;  
  285.     }  
  286.    
  287.     public DefaultHttpClient getHttpClient() {  
  288.         return httpClient;  
  289.     }  
  290.    
  291.     public enum HTTPMethod {  
  292.         GET, POST  
  293.     }  
  294.    
  295.     // SSL handler (ignore untrusted hosts)  
  296.     private static TrustManager truseAllManager = new X509TrustManager() {  
  297.         @Override  
  298.         public X509Certificate[] getAcceptedIssuers() {  
  299.             return null;  
  300.         }  
  301.    
  302.         @Override  
  303.         public void checkServerTrusted(X509Certificate[] chain, String authType)  
  304.                 throws CertificateException {  
  305.         }  
  306.    
  307.         @Override  
  308.         public void checkClientTrusted(X509Certificate[] chain, String authType)  
  309.                 throws CertificateException {  
  310.         }  
  311.     };  
  312. }  

最近研究了下HttpClient 4.0.1,主要是因为Java自己的HttpURLConnection对SSL支持的不好,而且控制起来不太方便,而且HttpClient还支持抓取非信任的站点,别的实现方式貌似需要在代码中显式导入证书。

需要的jar包:commons-logging-1.1.1.jar,httpclient-4.0.1.jar,httpcore-4.0.1.jar

Coding的时候遇到了些非常规问题:

1.HttpClient支持使用Java默认的Properties方式设置代理,不过我还是使用了HttpClient的代理设置方式。因而遇到了一个很诡异的问题,Properties方式设置的代理可以设置代理例外,即本地地址不通过代理访问,HttpClient没有简单的一句话设置的方法,必须写HttpRoutePlanner来自定义,比较繁琐。
如果使用HttpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,new HttpHost(proxyHost, proxyPort));来设置代理,则所有的请求都会往这个代理发送,没有例外,故弃之。

2.HTTP Header中的“Except”字段引起的问题,我向一些网页直接提交POST没有问题,但如果使用squid proxy进行post的话就会出现417 Expectation Failed错误,网上查了http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 发现这个问题是因为proxy server/web server不能理解或满足Except字段中指定的值,则会引发这个错误,而HttpClient默认会发送这个字段,只要指示request不发送这个字段即可。

3.不受信任的HTTPS站点的访问问题,通过X509TrustManager来完成,将方法重写成返回null的或者是啥都不做的,理论上要是checkServerTrusted或checkClientTrusted方法检测到不受信任的站点,会抛出异常,但如果什么都不做,则被视为通过检查。

4.写了个getTextContent方法,用来获取返回的文本,解决乱码问题的方法其实很简单,首先用正则提取HTTP Header中Content-Type里的charset,如果没有,使用默认编码分析html head中Content-Type里的charset,如果没有,使用系统默认编码。


第二篇博客(http://eyecm.com/httpclient-download/):

使用HttpClient下载文件
2012年08月03日  ⁄ 编程整理 ⁄ 共 1268字  ⁄ +0 ⁄ 被围观 2,737+

在之前的文章《HttpClient4.1入门教程-利用官方例子讲解httpClient4.1的用法》中介绍了HttpClient4的基本用户,本篇文章以实例的方式介绍如何使用HttpClient下载文件。

事实上我们仍然发送GET或者POST请求,但对响应写入到文件流中即可。通过这段代码,也许你就明白下载软件或采集器的写法了。

[java]  view plain copy
  1. package sitemap;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.InputStream;  
  6.   
  7. import org.apache.http.HttpResponse;  
  8. import org.apache.http.StatusLine;  
  9. import org.apache.http.client.HttpClient;  
  10. import org.apache.http.client.methods.HttpGet;  
  11. import org.apache.http.impl.client.DefaultHttpClient;  
  12.   
  13. public class SitemapDownloader  
  14. {  
  15. public static void main(String[] args) throws Exception  
  16. {  
  17. int min = 1;  
  18. int max = 806;  
  19.   
  20. String url = "http://www.foxnews.com/sitemap.xml?idx=";  
  21.   
  22. while (min < max)  
  23. {  
  24. Thread.sleep(500);  
  25.   
  26. HttpClient httpClient1 = new DefaultHttpClient();  
  27.   
  28. HttpGet httpGet1 = new HttpGet(url+min);  
  29. HttpResponse httpResponse1 = httpClient1.execute(httpGet1);  
  30.   
  31. StatusLine statusLine = httpResponse1.getStatusLine();  
  32. if(statusLine.getStatusCode() == 200)  
  33. {  
  34.   
  35. File xml = new File("d:/sitemap/"+min+".xml");  
  36.   
  37. FileOutputStream outputStream = new FileOutputStream(xml);  
  38. InputStream inputStream = httpResponse1.getEntity().getContent();  
  39. byte b[] = new byte[1024];  
  40. int j = 0;  
  41. while( (j = inputStream.read(b))!=-1)  
  42. {  
  43. outputStream.write(b,0,j);  
  44. }  
  45. outputStream.flush();  
  46. outputStream.close();  
  47.   
  48. min++;  
  49. System.out.println("存储了XML: " +min);  
  50. }  
  51.   
  52. httpClient1.getConnectionManager().shutdown();  
  53. }  
  54. }  
  55. }  

最后,将第一篇博客中的ssl认证功能加到第二篇博客中,就可以下载https资源:

[html]  view plain copy
  1. package com.adobe.touchstone.plus.updownload;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.InputStream;  
  6. import java.security.KeyManagementException;  
  7. import java.security.NoSuchAlgorithmException;  
  8. import java.security.cert.CertificateException;  
  9. import java.security.cert.X509Certificate;  
  10.   
  11. import javax.net.ssl.SSLContext;  
  12. import javax.net.ssl.TrustManager;  
  13. import javax.net.ssl.X509TrustManager;  
  14.   
  15. import org.apache.http.HttpResponse;  
  16. import org.apache.http.StatusLine;  
  17. import org.apache.http.client.HttpClient;  
  18. import org.apache.http.client.methods.HttpGet;  
  19. import org.apache.http.conn.scheme.Scheme;  
  20. import org.apache.http.conn.ssl.SSLSocketFactory;  
  21. import org.apache.http.impl.client.DefaultHttpClient;  
  22.   
  23.   
  24. public class SitemapDownloader{  
  25.     // SSL handler (ignore untrusted hosts)  
  26.     private static TrustManager truseAllManager = new X509TrustManager() {  
  27.         @Override  
  28.         public X509Certificate[] getAcceptedIssuers() {  
  29.             return null;  
  30.         }  
  31.    
  32.         @Override  
  33.         public void checkServerTrusted(X509Certificate[] chain, String authType)  
  34.                 throws CertificateException {  
  35.         }  
  36.    
  37.         @Override  
  38.         public void checkClientTrusted(X509Certificate[] chain, String authType)  
  39.                 throws CertificateException {  
  40.         }  
  41.     };  
  42.       
  43.     private static void enableSSL(HttpClient httpClient) {  
  44.         try {  
  45.             SSLContext sslcontext = SSLContext.getInstance("TLS");  
  46.             sslcontext.init(null, new TrustManager[] { truseAllManager }, null);  
  47.             SSLSocketFactory sf = new SSLSocketFactory(sslcontext);  
  48.             sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
  49.             Scheme https = new Scheme("https", sf, 443);  
  50.             httpClient.getConnectionManager().getSchemeRegistry()  
  51.                     .register(https);  
  52.         } catch (KeyManagementException e) {  
  53.             e.printStackTrace();  
  54.         } catch (NoSuchAlgorithmException e) {  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.     public static void main(String[] args) throws Exception{  
  59.       
  60.       
  61.         String url = "https://mail.google.com/mail/";  
  62.           
  63.         HttpClient httpClient1 = new DefaultHttpClient() ;  
  64.         enableSSL(httpClient1);  
  65.         HttpGet httpGet1 = new HttpGet(url);  
  66.         HttpResponse httpResponse1 = httpClient1.execute(httpGet1);  
  67.           
  68.         StatusLine statusLine = httpResponse1.getStatusLine();  
  69.         if(statusLine.getStatusCode() == 200){  
  70.             File xml = new File( "d:/mail.html" );  
  71.               
  72.             FileOutputStream outputStream = new FileOutputStream(xml);  
  73.             InputStream inputStream = httpResponse1.getEntity().getContent();  
  74.               
  75.             byte b[] = new byte[1024];  
  76.             int j = 0;  
  77.             while( (j = inputStream.read(b))!=-1){  
  78.                 System.out.println("Writing : "+b.toString());  
  79.                 outputStream.write(b,0,j);  
  80.             }  
  81.             outputStream.flush();  
  82.             outputStream.close();  
  83.               
  84.             System.out.println( "存储了文件: " +xml.toString());  
  85.             httpClient1.getConnectionManager(). shutdown();  
  86.         }  
  87.     }  

你可能感兴趣的:(如何使用HttpClient下载网络资源(包括下载ssl认证的资源))