HtmlUnit2.14使用样例—获取动态网页

标签: 简书笔记

转自:http://shihlei.iteye.com/blog/2067707

一、概述

HttpClient适合处理静态资源,网络爬虫等类似应用很大程度需要处理动态网页(内容有js填充,如百度图片,body里基本没有数据,碰到最麻烦的是新浪微博列表页)。将网页下载后,结合JS和Dom模型还原网页,我目前还未攻破,但在下载层还原网页,HtmlUnit是一种解决方案,虽然对JS的支持还是不完美。
HtmlUnit其实是自动化测试工具,集成了下载(HttpClient),Dom(NekoHtml),驱动JS(Rhino)。有一定的网页渲染能力,由于会驱动Dom,会消耗些CPU,内存。
本文描述HTMLUnit请求响应,设置cookies,设置代理,驱动JS等方法。

二、版本

pom.xml 依赖

<dependency>
            <groupId>net.sourceforge.htmlunit</groupId>
            <artifactId>htmlunit</artifactId>
            <version>2.14</version>
        </dependency>
        
        <dependency>
            <groupId>xml-apis</groupId>
            <artifactId>xml-apis</artifactId>
            <version>1.4.01</version>
        </dependency>
        
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.11.0</version>
        </dependency>

三、典型功能

1、打开百度


public static void main(String[] args) throws Exception {  
    String url = "http://www.baidu.com";  
  
    final WebClient webClient = new WebClient();  
    HtmlPage htmlPage = webClient.getPage(url);  
  
    // HtmlUnit dom模型  
    // 获取表单 ,获得form标签name属性=f  
    HtmlForm form = htmlPage.getFormByName("f");  
    // 获取输入框, 获取 input标签 ,name属性=q  
    HtmlTextInput text = form.getInputByName("q");  
    // 搜索百度  
    text.setText("baidu");  
    // 获取提交按钮  
    HtmlSubmitInput button = form.getInputByName("btnG");  
    // 提交表单  
    HtmlPage listPage = button.click();  
  
    System.out.println(listPage.asXml());  
           
      webClient.closeAllWindows();  
}  
   

2、获取动态页面

/** 
 * 获取百度图片js后的内容 
 *  
 * @throws Exception 
 */  
public void demo2() throws Exception {  
    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";  
  
    final WebClient webClient = new WebClient();  
  
    // 1 启动JS  
    webClient.getOptions().setJavaScriptEnabled(true);  
    // 2 禁用Css,可避免自动二次请求CSS进行渲染  
    webClient.getOptions().setCssEnabled(false);  
    // 3 启动客户端重定向  
    webClient.getOptions().setRedirectEnabled(true);  
  
    // 4 js运行错误时,是否抛出异常  
    webClient.getOptions().setThrowExceptionOnScriptError(false);  
    // 5 设置超时  
    webClient.getOptions().setTimeout(50000);  
      
    HtmlPage htmlPage = webClient.getPage(url);  
    // 等待JS驱动dom完成获得还原后的网页  
    webClient.waitForBackgroundJavaScript(10000);  
    // 网页内容  
    System.out.println(htmlPage.asXml());  
    webClient.closeAllWindows();  
}  

四、Demo

1 请求响应

/** 
 * Get请求 
 * @param url 
 * @return 
 * @throws Exception 
 */  
public static byte[] sendGetRequest(String url) throws Exception{  
        WebClient webClient = new WebClient();  
    WebRequest webRequest = new WebRequest(new URL(url));  
    webRequest.setHttpMethod(HttpMethod.GET);  
    return sendRequest(webClient,webRequest);  
}  
  
/** 
 * Post 请求 
 *  
 * @param url 
 * @param params 
 * @return 
 * @throws Exception 
 */  
public static byte[] sendPostRequest(String url,Map<String,String> params) throws Exception{  
        WebClient webClient = new WebClient();  
    WebRequest webRequest = new WebRequest(new URL(url));  
    webRequest.setHttpMethod(HttpMethod.POST);  
    if (params != null && params.size() > 0) {  
        for (Entry<String, String> param : params.entrySet()) {  
            webRequest.getRequestParameters().add(new NameValuePair(param.getKey(), param.getValue()));  
        }  
    }  
    return sendRequest(webClient,webRequest);  
}  
  
//底层请求  
private static byte[] sendRequest(WebClient webClient,WebRequest webRequest) throws Exception{  
    byte[] responseContent = null;  
    Page page = webClient.getPage(webRequest);  
      
    WebResponse webResponse = page.getWebResponse();  
      
    int status = webResponse.getStatusCode();  
      
    System.out.println("Charset : " + webResponse.getContentCharset());  
  
    System.out.println("ContentType : " + webResponse.getContentType());  
  
    // 读取数据内容  
    if (status==200) {  
        if (page.isHtmlPage()) {  
            <strong>// 等待JS执行完成,包括远程JS文件请求,Dom处理  
             webClient.waitForBackgroundJavaScript(10000);</strong>  
<strong>                     // 使用JS还原网页  
             responseContent = ((HtmlPage) page).asXml().getBytes();</strong>  
        } else {  
            InputStream bodyStream = webResponse.getContentAsStream();  
            responseContent = ByteStreams.toByteArray(bodyStream);  
            bodyStream.close();  
        }  
    }  
    // 关闭响应流  
    webResponse.cleanUp();  
  
    return responseContent;  
}  

2、配置JS,CSS,超时,重定向


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

3、代理


  private void setProxy(WebClient webClient,HttpProxy proxy) {  
    ProxyConfig proxyConfig = webClient.getOptions().getProxyConfig();  
    proxyConfig.setProxyHost(proxy.getHost());  
    proxyConfig.setProxyPort(proxy.getPort());  
  
    DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient  
            .getCredentialsProvider();  
    credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());  
}  

4、辅助类


public class HttpProxy {
    private String proxy = "http";
    private String host;
    private int port;
    private String user;
    private String password;

    public String getProxy() {
        return proxy;
    }

    public void setProxy(String proxy) {
        this.proxy = proxy;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

五、Cookies:可以用于认证数据设置

1)设置Cookies


private void setCookies(WebClient webClient,String domain, Map<String, String> cookies) {  
    if (cookies != null && cookies.size() > 0) {  
        webClient.getCookieManager().setCookiesEnabled(true);// enable  
                                                                // cookies  
        for (Entry<String, String> c : cookies.entrySet()) {  
            Cookie cookie = new Cookie(domain, c.getKey(), c.getValue());  
            webClient.getCookieManager().addCookie(cookie);  
        }  
    }  
}  


2)获取响应Cookies


private Map<String, String> getResponseCookies(WebClient webClient) {  
    Set<Cookie> cookies = webClient.getCookieManager().getCookies();  
    Map<String, String> responseCookies = Maps.newHashMap();  
    for (Cookie c : cookies) {  
        responseCookies.put(c.getName(), c.getValue());  
    }  
    return responseCookies;  
}  

3)删除所有Cookies


/** 
 * 清除所有cookie 
 */  
public void clearCookies(WebClient webClient) {  
    webClient.getCookieManager().clearCookies();  
}  

六、驱动JS

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


public void doWeb(Page page) {  
    if (page instanceof HtmlPage) {  
        StringBuilder js = new StringBuilder();  
        js.append("document.getElementsByName('username')[1].value='").append(WeiboAccount.USERNAME)  
                .append("';");  
        js.append("document.getElementsByName('password')[1].value='").append(WeiboAccount.PASSWORD)  
                .append("';");  
        js.append("document.getElementsByClassName('W_btn_g')[1].click();");  
        HtmlPage htmlPage = (HtmlPage) page;  
        htmlPage.executeJavaScript(js.toString());  
    }  
} 

七、附录:完整代码

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.util.Cookie;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.jdd.basedata.commons.bean.HttpProxy;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class HtmlUnitDemo {  
  
    private WebClient webClient = null;  
  
    private int timeout = 50000;  
  
    public HtmlUnitDemo() {  
        this(null);  
    }  
  
    /** 
     * Get请求 
     *  
     * @param url 
     * @return 
     * @throws Exception 
     */  
    public byte[] sendGetRequest(String url) throws Exception {  
        WebRequest webRequest = new WebRequest(new URL(url));  
        webRequest.setHttpMethod(HttpMethod.GET);  
        return sendRequest(webRequest);  
    }  
  
    /** 
     * Post 请求 
     *  
     * @param url 
     * @param params 
     * @return 
     * @throws Exception 
     */  
    public byte[] sendPostRequest(String url, Map<String, String> params) throws Exception {  
        WebRequest webRequest = new WebRequest(new URL(url));  
        webRequest.setHttpMethod(HttpMethod.POST);  
        if (params != null && params.size() > 0) {  
            for (Entry<String, String> param : params.entrySet()) {  
                webRequest.getRequestParameters().add(new NameValuePair(param.getKey(), param.getValue()));  
            }  
        }  
        return sendRequest(webRequest);  
    }  
  
    // 底层请求  
    private byte[] sendRequest(WebRequest webRequest) throws Exception {  
        byte[] responseContent = null;  
        Page page = webClient.getPage(webRequest);  
  
        WebResponse webResponse = page.getWebResponse();  
  
        int status = webResponse.getStatusCode();  
  
        System.out.println("Charset : " + webResponse.getContentCharset());  
  
        System.out.println("ContentType : " + webResponse.getContentType());  
  
        // 读取数据内容  
        if (status == 200) {  
            if (page.isHtmlPage()) {  
                // 等待JS执行完成  
                webClient.waitForBackgroundJavaScript(100000);  
                responseContent = ((HtmlPage) page).asXml().getBytes();  
            } else {  
                InputStream bodyStream = webResponse.getContentAsStream();  
                responseContent = ByteStreams.toByteArray(bodyStream);  
                bodyStream.close();  
            }  
        }  
        // 关闭响应流  
        webResponse.cleanUp();  
  
        return responseContent;  
    }  
  
    public HtmlUnitDemo(HttpProxy proxy) {  
        webClient = new WebClient();  
        configWebClient();  
        // 设置代理  
        if (proxy != null) {  
            setProxy(proxy);  
        }  
    }  
  
    private void configWebClient() {  
        // 设置webClient的相关参数  
        // 1 启动JS  
        webClient.getOptions().setJavaScriptEnabled(true);  
        // 2 禁用Css,可避免自动二次请求CSS进行渲染  
        webClient.getOptions().setCssEnabled(false);  
        // 3 启动客户端重定向  
        webClient.getOptions().setRedirectEnabled(true);  
  
        // 4 js运行错误时,是否抛出异常  
        webClient.getOptions().setThrowExceptionOnScriptError(false);  
        // 5 设置超时  
        webClient.getOptions().setTimeout(timeout);  
    }  
  
    private void setProxy(HttpProxy proxy) {  
        ProxyConfig proxyConfig = webClient.getOptions().getProxyConfig();  
        proxyConfig.setProxyHost(proxy.getHost());  
        proxyConfig.setProxyPort(proxy.getPort());  
  
        DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient  
                .getCredentialsProvider();  
        credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());  
    }  
  
    @SuppressWarnings("unused")  
    private Map<String, String> getResponseCookies() {  
        Set<Cookie> cookies = webClient.getCookieManager().getCookies();  
        Map<String, String> responseCookies = Maps.newHashMap();  
        for (Cookie c : cookies) {  
            responseCookies.put(c.getName(), c.getValue());  
        }  
        return responseCookies;  
    }  
  
    @SuppressWarnings("unused")  
    private void setCookies(String domain, Map<String, String> cookies) {  
        if (cookies != null && cookies.size() > 0) {  
            webClient.getCookieManager().setCookiesEnabled(true);// enable  
                                                                    // cookies  
            for (Entry<String, String> c : cookies.entrySet()) {  
                Cookie cookie = new Cookie(domain, c.getKey(), c.getValue());  
                webClient.getCookieManager().addCookie(cookie);  
  
                System.out.println("Set Cookies : " + c.getKey() + " | " + c.getValue());  
            }  
        }  
    }  
  
    /** 
     * 清除所有cookie 
     */  
    public void clearCookies() {  
        webClient.getCookieManager().clearCookies();  
    }  
  
    public void shutdown() throws IOException {  
        webClient.closeAllWindows();  
    }  
  
    /** 
     * 打开google 搜索百度 
     *  
     * @param args 
     * @throws Exception 
     */  
    public void demo() throws Exception{  
        String url = "http://www.google.com.hk";  
  
        final WebClient webClient = new WebClient();  
        HtmlPage htmlPage = webClient.getPage(url);  
  
        // HtmlUnit dom模型  
        // 获取表单 ,获得form标签name属性=f  
        HtmlForm form = htmlPage.getFormByName("f");  
        // 获取输入框, 获取 input标签 ,name属性=q  
        HtmlTextInput text = form.getInputByName("q");  
        // 搜索百度  
        text.setText("baidu");  
        // 获取提交按钮  
        HtmlSubmitInput button = form.getInputByName("btnG");  
        // 提交表单  
        HtmlPage listPage = button.click();  
  
        System.out.println(listPage.asXml());  
        webClient.closeAllWindows();  
    }  
    
    /** 
     * 打开百度 
     *  
     * @param args 
     * @throws Exception 
     */  
    public static void main(String[] args) throws Exception {  
        String url = "http://www.baidu.com";  
  
        HtmlUnitDemo htmlUnit = new HtmlUnitDemo();  
        byte[] getResponse = htmlUnit.sendGetRequest(url);  
        System.out.println("Get Body : " + new String(getResponse, "utf-8"));  
        byte[] postResponse = htmlUnit.sendPostRequest(url, null);  
        System.out.println("Get Body : " + new String(postResponse, "utf-8"));  
  
        htmlUnit.shutdown();  
    }  
}  

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 119,397评论 1 241
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 52,183评论 1 200
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 74,775评论 0 167
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 36,619评论 0 127
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 43,458评论 1 206
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 36,068评论 1 126
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 28,056评论 2 209
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 27,184评论 0 120
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 26,124评论 5 173
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 30,241评论 0 178
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 27,450评论 1 170
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 28,689评论 1 178
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 22,990评论 0 25
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 25,606评论 2 164
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 29,463评论 3 173
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 24,454评论 0 4
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 24,469评论 0 113
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 30,687评论 2 189
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 31,094评论 2 188

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 133,751评论 18 139
  • 前端开发面试知识点大纲: HTML&CSS: 对Web标准的理解、浏览器内核差异、兼容性、hack、CSS基本功:...
    秀才JaneBook阅读 2,172评论 0 25
  • 第三部 地名:那个姓氏 >> 忽然间,在一块阴沉沉的石头上,我虽然没有见到稍微光亮一点的颜色,却感觉到有一条摇曳不...
    懒兔少女阅读 191评论 0 0
  • 足月儿:妊娠满37周(260天)以上,不足42周,出生体重大于2500g(2500~4000g),头围33~35厘...
    六月一页书阅读 249评论 0 0
  • ❶心在哪 钱就在哪! 现在好多姑娘都说:“我不是冲他的钱,我就是图他这个人,他给不给我花钱我都不在乎!”那鱼哥告诉...
    Fish鱼哥哥阅读 221评论 0 1