使用HtmlUnit做爬虫

一 概述

 

    HttpClient适合处理静态资源,网络爬虫等类似应用很大程度需要处理动态网页(内容有js填充,如百度图片,body里基本没有数据,碰到最麻烦的是新浪微博列表页)。但在下载层还原网页,HtmlUnit是一种解决方案,虽然对JS的支持还是不完美。

    HtmlUnit其实是自动化测试工具,集成了下载(HttpClient),Dom(NekoHtml),驱动JS(Rhino)。有一定的网页渲染能力,由于会驱动Dom,会消耗些CPU,内存。

   本文描述HTMLUnit请求响应,设置cookies,设置代理,驱动JS等方法。

 

二 版本

 

Xml代码   收藏代码
  1. <dependency>  
  2.     <groupId>net.sourceforge.htmlunitgroupId>  
  3.     <artifactId>htmlunitartifactId>  
  4.     <version>2.14version>  
  5. dependency>  

 

三 典型功能

 

1) 打开google搜索百度

 

Java代码   收藏代码
  1. /** 
  2.  * 打开google 搜索百度 
  3.  *  
  4.  * @param args 
  5.  * @throws Exception 
  6.  */  
  7. public static void main(String[] args) throws Exception {  
  8.     String url = "http://www.google.com.hk";  
  9.   
  10.     final WebClient webClient = new WebClient();  
  11.     HtmlPage htmlPage = webClient.getPage(url);  
  12.   
  13.     // HtmlUnit dom模型  
  14.     // 获取表单 ,获得form标签name属性=f  
  15.     HtmlForm form = htmlPage.getFormByName("f");  
  16.     // 获取输入框, 获取 input标签 ,name属性=q  
  17.     HtmlTextInput text = form.getInputByName("q");  
  18.     // 搜索百度  
  19.     text.setText("baidu");  
  20.     // 获取提交按钮  
  21.     HtmlSubmitInput button = form.getInputByName("btnG");  
  22.     // 提交表单  
  23.     HtmlPage listPage = button.click();  
  24.   
  25.     System.out.println(listPage.asXml());  
  26.            
  27.       webClient.closeAllWindows();  
  28. }  

 

2)获取动态页面

 

Java代码   收藏代码
  1. /** 
  2.  * 获取百度图片js后的内容 
  3.  *  
  4.  * @throws Exception 
  5.  */  
  6. public void demo2() throws Exception {  
  7.     String url = "http://image.baidu.com/i?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1400328281672_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=html";  
  8.   
  9.     final WebClient webClient = new WebClient();  
  10.   
  11.     // 1 启动JS  
  12.     webClient.getOptions().setJavaScriptEnabled(true);  
  13.     // 2 禁用Css,可避免自动二次请求CSS进行渲染  
  14.     webClient.getOptions().setCssEnabled(false);  
  15.     // 3 启动客户端重定向  
  16.     webClient.getOptions().setRedirectEnabled(true);  
  17.   
  18.     // 4 js运行错误时,是否抛出异常  
  19.     webClient.getOptions().setThrowExceptionOnScriptError(false);  
  20.     // 5 设置超时  
  21.     webClient.getOptions().setTimeout(50000);  
  22.       
  23.     HtmlPage htmlPage = webClient.getPage(url);  
  24.     // 等待JS驱动dom完成获得还原后的网页  
  25.     webClient.waitForBackgroundJavaScript(10000);  
  26.     // 网页内容  
  27.     System.out.println(htmlPage.asXml());  
  28.     webClient.closeAllWindows();  
  29. }  

 

四 样例

(1)请求响应

 

Java代码   收藏代码
  1. /** 
  2.  * Get请求 
  3.  * @param url 
  4.  * @return 
  5.  * @throws Exception 
  6.  */  
  7. public static byte[] sendGetRequest(String url) throws Exception{  
  8.         WebClient webClient = new WebClient();  
  9.     WebRequest webRequest = new WebRequest(new URL(url));  
  10.     webRequest.setHttpMethod(HttpMethod.GET);  
  11.     return sendRequest(webClient,webRequest);  
  12. }  
  13.   
  14. /** 
  15.  * Post 请求 
  16.  *  
  17.  * @param url 
  18.  * @param params 
  19.  * @return 
  20.  * @throws Exception 
  21.  */  
  22. public static byte[] sendPostRequest(String url,Map params) throws Exception{  
  23.         WebClient webClient = new WebClient();  
  24.     WebRequest webRequest = new WebRequest(new URL(url));  
  25.     webRequest.setHttpMethod(HttpMethod.POST);  
  26.     if (params != null && params.size() > 0) {  
  27.         for (Entry param : params.entrySet()) {  
  28.             webRequest.getRequestParameters().add(new NameValuePair(param.getKey(), param.getValue()));  
  29.         }  
  30.     }  
  31.     return sendRequest(webClient,webRequest);  
  32. }  
  33.   
  34. //底层请求  
  35. private static byte[] sendRequest(WebClient webClient,WebRequest webRequest) throws Exception{  
  36.     byte[] responseContent = null;  
  37.     Page page = webClient.getPage(webRequest);  
  38.       
  39.     WebResponse webResponse = page.getWebResponse();  
  40.       
  41.     int status = webResponse.getStatusCode();  
  42.       
  43.     System.out.println("Charset : " + webResponse.getContentCharset());  
  44.   
  45.     System.out.println("ContentType : " + webResponse.getContentType());  
  46.   
  47.     // 读取数据内容  
  48.     if (status==200) {  
  49.         if (page.isHtmlPage()) {  
  50.             // 等待JS执行完成,包括远程JS文件请求,Dom处理  
  51.              webClient.waitForBackgroundJavaScript(10000);  
  52.                      // 使用JS还原网页  
  53.              responseContent = ((HtmlPage) page).asXml().getBytes();  
  54.         } else {  
  55.             InputStream bodyStream = webResponse.getContentAsStream();  
  56.             responseContent = ByteStreams.toByteArray(bodyStream);  
  57.             bodyStream.close();  
  58.         }  
  59.     }  
  60.     // 关闭响应流  
  61.     webResponse.cleanUp();  
  62.   
  63.     return responseContent;  
  64. }  

 

(2)配置JS,CSS,超时,重定向

 

Java代码   收藏代码
  1. private void configWebClient(WebClient webClient) {  
  2.     // 设置webClient的相关参数  
  3.     // 1 启动JS  
  4.     webClient.getOptions().setJavaScriptEnabled(true);  
  5.     // 2 禁用Css,可避免自动二次请求CSS进行渲染  
  6.     webClient.getOptions().setCssEnabled(false);  
  7.     // 3 启动客户端重定向  
  8.     webClient.getOptions().setRedirectEnabled(true);  
  9.   
  10.     // 4 js运行错误时,是否抛出异常  
  11.     webClient.getOptions().setThrowExceptionOnScriptError(false);  
  12.     // 5 设置超时  
  13.     webClient.getOptions().setTimeout(timeout);  
  14. }  

 

(3)代理

 

Java代码   收藏代码
  1. private void setProxy(WebClient webClient,HttpProxy proxy) {  
  2.     ProxyConfig proxyConfig = webClient.getOptions().getProxyConfig();  
  3.     proxyConfig.setProxyHost(proxy.getHost());  
  4.     proxyConfig.setProxyPort(proxy.getPort());  
  5.   
  6.     DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient  
  7.             .getCredentialsProvider();  
  8.     credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());  
  9. }  

 

 辅助类:

 

Java代码   收藏代码
  1. package x.http.core;  
  2.   
  3.   
  4. /** 
  5.  * Http代理 
  6.  *  
  7.  * @author shilei 
  8.  *  
  9.  */  
  10. public class HttpProxy {  
  11.     private String proxy = "http";  
  12.     private String host;  
  13.     private int port;  
  14.     private String user;  
  15.     private String password;  
  16.     public String getProxy() {  
  17.         return proxy;  
  18.     }  
  19.     public void setProxy(String proxy) {  
  20.         this.proxy = proxy;  
  21.     }  
  22.     public String getHost() {  
  23.         return host;  
  24.     }  
  25.     public void setHost(String host) {  
  26.         this.host = host;  
  27.     }  
  28.     public int getPort() {  
  29.         return port;  
  30.     }  
  31.     public void setPort(int port) {  
  32.         this.port = port;  
  33.     }  
  34.     public String getUser() {  
  35.         return user;  
  36.     }  
  37.     public void setUser(String user) {  
  38.         this.user = user;  
  39.     }  
  40.     public String getPassword() {  
  41.         return password;  
  42.     }  
  43.     public void setPassword(String password) {  
  44.         this.password = password;  
  45.     }  
  46. }  

 

(4)Cookies:可以用于认证数据设置

1)设置Cookies

 

Java代码   收藏代码
  1. private void setCookies(WebClient webClient,String domain, Map cookies) {  
  2.     if (cookies != null && cookies.size() > 0) {  
  3.         webClient.getCookieManager().setCookiesEnabled(true);// enable  
  4.                                                                 // cookies  
  5.         for (Entry c : cookies.entrySet()) {  
  6.             Cookie cookie = new Cookie(domain, c.getKey(), c.getValue());  
  7.             webClient.getCookieManager().addCookie(cookie);  
  8.         }  
  9.     }  
  10. }  

 

2)获取响应Cookies

 

Java代码   收藏代码
  1. private Map getResponseCookies(WebClient webClient) {  
  2.     Set cookies = webClient.getCookieManager().getCookies();  
  3.     Map responseCookies = Maps.newHashMap();  
  4.     for (Cookie c : cookies) {  
  5.         responseCookies.put(c.getName(), c.getValue());  
  6.     }  
  7.     return responseCookies;  
  8. }  

 

3)删除所有Cookies

 

Java代码   收藏代码
  1. /** 
  2.  * 清除所有cookie 
  3.  */  
  4. public void clearCookies(WebClient webClient) {  
  5.     webClient.getCookieManager().clearCookies();  
  6. }  

 

 (5)驱动JS:

可实现自动化流程,如驱动表单提交,获取表单提交后的页面

如登录后页面:

Java代码   收藏代码
  1. public void doWeb(Page page) {  
  2.     if (page instanceof HtmlPage) {  
  3.         StringBuilder js = new StringBuilder();  
  4.         js.append("document.getElementsByName('username')[1].value='").append(WeiboAccount.USERNAME)  
  5.                 .append("';");  
  6.         js.append("document.getElementsByName('password')[1].value='").append(WeiboAccount.PASSWORD)  
  7.                 .append("';");  
  8.         js.append("document.getElementsByClassName('W_btn_g')[1].click();");  
  9.         HtmlPage htmlPage = (HtmlPage) page;  
  10.         htmlPage.executeJavaScript(js.toString());  
  11.     }  
  12. }  

 

 附录:完整代码

Java代码   收藏代码
  1. package x.http.simple.htmlunit;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.net.URL;  
  6. import java.util.Map;  
  7. import java.util.Map.Entry;  
  8. import java.util.Set;  
  9.   
  10. import x.http.core.HttpProxy;  
  11.   
  12. import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;  
  13. import com.gargoylesoftware.htmlunit.HttpMethod;  
  14. import com.gargoylesoftware.htmlunit.Page;  
  15. import com.gargoylesoftware.htmlunit.ProxyConfig;  
  16. import com.gargoylesoftware.htmlunit.WebClient;  
  17. import com.gargoylesoftware.htmlunit.WebRequest;  
  18. import com.gargoylesoftware.htmlunit.WebResponse;  
  19. import com.gargoylesoftware.htmlunit.html.HtmlForm;  
  20. import com.gargoylesoftware.htmlunit.html.HtmlPage;  
  21. import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;  
  22. import com.gargoylesoftware.htmlunit.html.HtmlTextInput;  
  23. import com.gargoylesoftware.htmlunit.util.Cookie;  
  24. import com.gargoylesoftware.htmlunit.util.NameValuePair;  
  25. import com.google.common.collect.Maps;  
  26. import com.google.common.io.ByteStreams;  
  27.   
  28. public class HtmlUnitDemo {  
  29.   
  30.     private WebClient webClient = null;  
  31.   
  32.     private int timeout = 50000;  
  33.   
  34.     public HtmlUnitDemo() {  
  35.         this(null);  
  36.     }  
  37.   
  38.     /** 
  39.      * Get请求 
  40.      *  
  41.      * @param url 
  42.      * @return 
  43.      * @throws Exception 
  44.      */  
  45.     public byte[] sendGetRequest(String url) throws Exception {  
  46.         WebRequest webRequest = new WebRequest(new URL(url));  
  47.         webRequest.setHttpMethod(HttpMethod.GET);  
  48.         return sendRequest(webRequest);  
  49.     }  
  50.   
  51.     /** 
  52.      * Post 请求 
  53.      *  
  54.      * @param url 
  55.      * @param params 
  56.      * @return 
  57.      * @throws Exception 
  58.      */  
  59.     public byte[] sendPostRequest(String url, Map params) throws Exception {  
  60.         WebRequest webRequest = new WebRequest(new URL(url));  
  61.         webRequest.setHttpMethod(HttpMethod.POST);  
  62.         if (params != null && params.size() > 0) {  
  63.             for (Entry param : params.entrySet()) {  
  64.                 webRequest.getRequestParameters().add(new NameValuePair(param.getKey(), param.getValue()));  
  65.             }  
  66.         }  
  67.         return sendRequest(webRequest);  
  68.     }  
  69.   
  70.     // 底层请求  
  71.     private byte[] sendRequest(WebRequest webRequest) throws Exception {  
  72.         byte[] responseContent = null;  
  73.         Page page = webClient.getPage(webRequest);  
  74.   
  75.         WebResponse webResponse = page.getWebResponse();  
  76.   
  77.         int status = webResponse.getStatusCode();  
  78.   
  79.         System.out.println("Charset : " + webResponse.getContentCharset());  
  80.   
  81.         System.out.println("ContentType : " + webResponse.getContentType());  
  82.   
  83.         // 读取数据内容  
  84.         if (status == 200) {  
  85.             if (page.isHtmlPage()) {  
  86.                 // 等待JS执行完成  
  87.                 webClient.waitForBackgroundJavaScript(100000);  
  88.                 responseContent = ((HtmlPage) page).asXml().getBytes();  
  89.             } else {  
  90.                 InputStream bodyStream = webResponse.getContentAsStream();  
  91.                 responseContent = ByteStreams.toByteArray(bodyStream);  
  92.                 bodyStream.close();  
  93.             }  
  94.         }  
  95.         // 关闭响应流  
  96.         webResponse.cleanUp();  
  97.   
  98.         return responseContent;  
  99.     }  
  100.   
  101.     public HtmlUnitDemo(HttpProxy proxy) {  
  102.         webClient = new WebClient();  
  103.         configWebClient();  
  104.         // 设置代理  
  105.         if (proxy != null) {  
  106.             setProxy(proxy);  
  107.         }  
  108.     }  
  109.   
  110.     private void configWebClient() {  
  111.         // 设置webClient的相关参数  
  112.         // 1 启动JS  
  113.         webClient.getOptions().setJavaScriptEnabled(true);  
  114.         // 2 禁用Css,可避免自动二次请求CSS进行渲染  
  115.         webClient.getOptions().setCssEnabled(false);  
  116.         // 3 启动客户端重定向  
  117.         webClient.getOptions().setRedirectEnabled(true);  
  118.   
  119.         // 4 js运行错误时,是否抛出异常  
  120.         webClient.getOptions().setThrowExceptionOnScriptError(false);  
  121.         // 5 设置超时  
  122.         webClient.getOptions().setTimeout(timeout);  
  123.     }  
  124.   
  125.     private void setProxy(HttpProxy proxy) {  
  126.         ProxyConfig proxyConfig = webClient.getOptions().getProxyConfig();  
  127.         proxyConfig.setProxyHost(proxy.getHost());  
  128.         proxyConfig.setProxyPort(proxy.getPort());  
  129.   
  130.         DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient  
  131.                 .getCredentialsProvider();  
  132.         credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());  
  133.     }  
  134.   
  135.     @SuppressWarnings("unused")  
  136.     private Map getResponseCookies() {  
  137.         Set cookies = webClient.getCookieManager().getCookies();  
  138.         Map responseCookies = Maps.newHashMap();  
  139.         for (Cookie c : cookies) {  
  140.             responseCookies.put(c.getName(), c.getValue());  
  141.         }  
  142.         return responseCookies;  
  143.     }  
  144.   
  145.     @SuppressWarnings("unused")  
  146.     private void setCookies(String domain, Map cookies) {  
  147.         if (cookies != null && cookies.size() > 0) {  
  148.             webClient.getCookieManager().setCookiesEnabled(true);// enable  
  149.                                                                     // cookies  
  150.             for (Entry c : cookies.entrySet()) {  
  151.                 Cookie cookie = new Cookie(domain, c.getKey(), c.getValue());  
  152.                 webClient.getCookieManager().addCookie(cookie);  
  153.   
  154.                 System.out.println("Set Cookies : " + c.getKey() + " | " + c.getValue());  
  155.             }  
  156.         }  
  157.     }  
  158.   
  159.     /** 
  160.      * 清除所有cookie 
  161.      */  
  162.     public void clearCookies() {  
  163.         webClient.getCookieManager().clearCookies();  
  164.     }  
  165.   
  166.     public void shutdown() throws IOException {  
  167.         webClient.closeAllWindows();  
  168.     }  
  169.   
  170.     /** 
  171.      * 打开google 搜索百度 
  172.      *  
  173.      * @param args 
  174.      * @throws Exception 
  175.      */  
  176.     public void demo() throws Exception{  
  177.         String url = "http://www.google.com.hk";  
  178.   
  179.         final WebClient webClient = new WebClient();  
  180.         HtmlPage htmlPage = webClient.getPage(url);  
  181.   
  182.         // HtmlUnit dom模型  
  183.         // 获取表单 ,获得form标签name属性=f  
  184.         HtmlForm form = htmlPage.getFormByName("f");  
  185.         // 获取输入框, 获取 input标签 ,name属性=q  
  186.         HtmlTextInput text = form.getInputByName("q");  
  187.         // 搜索百度  
  188.         text.setText("baidu");  
  189.         // 获取提交按钮  
  190.         HtmlSubmitInput button = form.getInputByName("btnG");  
  191.         // 提交表单  
  192.         HtmlPage listPage = button.click();  
  193.   
  194.         System.out.println(listPage.asXml());  
  195.         webClient.closeAllWindows();  
  196.     }  
  197.     /** 
  198.      * 打开google 搜索百度 
  199.      *  
  200.      * @param args 
  201.      * @throws Exception 
  202.      */  
  203.     public static void main(String[] args) throws Exception {  
  204.         String url = "http://www.google.com.hk";  
  205.   
  206.         HtmlUnitDemo htmlUnit = new HtmlUnitDemo();  
  207.         byte[] getResponse = htmlUnit.sendGetRequest(url);  
  208.         System.out.println("Get Body : " + new String(getResponse, "utf-8"));  
  209.         byte[] postResponse = htmlUnit.sendPostRequest(url, null);  
  210.         System.out.println("Get Body : " + new String(postResponse, "utf-8"));  
  211.   
  212.         htmlUnit.shutdown();  
  213.     }  
  214. }  

 本文转载至http://shihlei.iteye.com/blog/2067707

你可能感兴趣的:(Java)