`
Foxswily
  • 浏览: 76412 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

HtmlUnit实现的网站登录

阅读更多

最近坛子里接连出现基于httpclient登录网站的帖子,也凑个热闹,分享一点基于htmlunit的登录经验

谨以此文祭奠我刚刚逝去的鼠标

----------------------------------------------分割线---------------------------------------------------
HtmlUnit 目前最新版本2.7(2010-04-15 Foxswily本人确认)
基于httpclient封装(甚至已经做好启用httpclient4的准备),模拟浏览器操作,JavaScript支持较全面,包括主流的jQuery类库,这也是它的强大之处,一般网站的JS屏蔽可以轻松突破。

举例说明

    //创建浏览器,可以选择IE、FF等等
    WebClient client = new WebClient(BrowserVersion.INTERNET_EXPLORER_7);
   
    //获取某网站页面
    HtmlPage page = client.getPage("http://xxx.com");
   
    //获取某页面元素,可通过id或name,(具体方式很多 --Foxswily)
    HtmlElement elmt = page.getElementById("someid");
    //HtmlElement elmt = page.getElementByName("somename");
   
    //此例以文本框为例,先点击,再输入,完全跟真浏览器行为一致
    elmt.click();
    elmt.type("somewords");
   
    //获取按钮
    HtmlButton loginBtn = (HtmlButton)page.getElementById("btnId");
    //点击并获得返回结果
    Page resultPage = loginBtn.click();
    //结果拿到了,想干啥您随意
    log.debug(resultPage.getWebResponse().getContentAsString());
    

 


沿着这个思路展开一下,模拟登录不再需要破解什么js逻辑,用户实际做什么代码就模拟什么,轻松多了   

额外的友情提示,Foxswily本人曾在登录用户量众多的discuz论坛时发现个小问题(已提交bug)

造成登录后跳转失效,如有雷同参照解决吧

问题描述
    HtmlPage.executeRefreshIfNeeded()
when html header has meta like "<META HTTP-EQUIV="Refresh" CONTENT="3 URL=h
ttp://www.some.org/some.html">" it throws NumberFormatException.
cause there is no ";" after "3" in the content.
some forum sites have this bad writting html page.

大意就是,自动跳转格式有问题,htmlunit解析不了,直接Exception了,改写HtmlPage的一个方法后通过。

    private void executeRefreshIfNeeded() throws IOException {
        // If this page is not in a frame then a refresh has already happened,
        // most likely through the JavaScript onload handler, so we don't do a
        // second refresh.
        final WebWindow window = getEnclosingWindow();
        if (window == null) {
            return;
        }

        final String refreshString = getRefreshStringOrNull();
        if (refreshString == null || refreshString.length() == 0) {
            return;
        }

        final double time;
        final URL url;

        int index = refreshString.indexOf(";");
        final boolean timeOnly = (index == -1);

        if (timeOnly && refreshString.indexOf(" ") == -1) {
            // Format: <meta http-equiv='refresh' content='10'>
            try {
                time = Double.parseDouble(refreshString);
            } catch (final NumberFormatException e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Malformed refresh string (no ';' but not a number): "
                            + refreshString, e);
                }
                return;
            }
            url = getWebResponse().getRequestSettings().getUrl();
        } else {
            if (refreshString.indexOf(";") == -1) {
                index = refreshString.indexOf(" ");
            }
            // Format: <meta http-equiv='refresh'
            // content='10;url=http://www.blah.com'>
            try {
                time = Double.parseDouble(refreshString.substring(0, index).trim());
            } catch (final NumberFormatException e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Malformed refresh string (no valid number before ';') "
                            + refreshString, e);
                }
                return;
            }
            index = refreshString.toLowerCase().indexOf("url=", index);
            if (index == -1) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Malformed refresh string (found ';' but no 'url='): "
                            + refreshString);
                }
                return;
            }
            final StringBuilder buffer = new StringBuilder(refreshString
                    .substring(index + 4));
            if (buffer.toString().trim().length() == 0) {
                // content='10; URL=' is treated as content='10'
                url = getWebResponse().getRequestSettings().getUrl();
            } else {
                if (buffer.charAt(0) == '"' || buffer.charAt(0) == 0x27) {
                    buffer.deleteCharAt(0);
                }
                if (buffer.charAt(buffer.length() - 1) == '"'
                        || buffer.charAt(buffer.length() - 1) == 0x27) {
                    buffer.deleteCharAt(buffer.length() - 1);
                }
                final String urlString = buffer.toString();
                try {
                    url = getFullyQualifiedUrl(urlString);
                } catch (final MalformedURLException e) {
                    if (LOG.isErrorEnabled()) {
                        LOG.error("Malformed URL in refresh string: " + refreshString, e);
                    }
                    throw e;
                }
            }
        }

        final int timeRounded = (int) time;
        getWebClient().getRefreshHandler().handleRefresh(this, url, timeRounded);
    }
 




分享到:
评论
27 楼 ricien 2013-06-27  
我登录人人之类的都登录不上去,无法跳转请问一下是什么问题啊
26 楼 jccmjl 2012-04-10  
<frameset cols="*,1024,*" border="0px" frameborder="0" framespacing="0px" > 
  <frame src="blank.jsp" scrolling="no" noresize>
  <frameset rows="90,*" cols="*" frameborder="NO" border="0" framespacing="0">
    <frame src="header.jsp" rows="90,*" scrolling="no" noresize name="header"  >
    <frameset cols="200,*" frameborder="NO" border="0" framespacing="0">
      <frame src="menu.faces" scrolling="auto" noresize  name="menu"  >
      <frame src="./SysManager/bulletin.faces?bbstype=2" scrolling="auto" noresize  name="body"  >
    </frameset>
   </frameset>







我登录成功以后返回到frame框架这里,不知道如何往下写了》。。。。
25 楼 alosin 2011-03-21  
所以已经放弃HtmlUnit了!!
24 楼 Foxswily 2011-03-16  
诸如此类的麻烦挺多,随便一个不起眼的地方对解析都是灾难
23 楼 alosin 2011-03-15  
Foxswily 写道
看懂了HtmlUnit自然知道这种提交和普通按钮没什么区别,仅仅在于你取的是Link还是Button

那个问题用htmlpage.executeJavaScript(script code)解决了,但最近遇到一个错误(用的是HtmlUnit 2.8):
警告: Expected content type of 'application/javascript' or 'application/ecmascript' for remotely loaded JavaScript element at 'http://www.ibcbet.com/commjs/ieupdate.js', but got 'application/x-javascript'.
似乎是个BUG,htmlunit作者承认的
22 楼 Foxswily 2011-03-10  
看懂了HtmlUnit自然知道这种提交和普通按钮没什么区别,仅仅在于你取的是Link还是Button
21 楼 alosin 2011-03-09  
那想请问一下,如果遇到直接通过js将Form提交的,有没有好的解决办法
比如:<a href="#" click="return submit()"></a>
还有没搞清楚,为什么HtmlForm没有直接submit的方法。。。
20 楼 buaastorm 2010-12-03  
第一个问题我最后通过getByValue解决了,第二个我也实在没有办法,一直没弄明白HttpClient的机制,导致老是连接不到服务器。不过看到你的帖子还是很受启发,thx。
19 楼 Foxswily 2010-12-03  
第一个问题,HtmlPage的方法getElementsByTagName(String tagName),按tag慢慢过滤,现在的页面不带id、name也够少见了

第二个问题,HttpClient和HtmlUnit不是一个层次上的,HtmlUnit底层使用了HttpClient,他方便的地方在于Html和JS的解析。
18 楼 buaastorm 2010-12-02  
对了,不知道你对HttpClient熟悉吗?其实如果这个功能能用HttpClient来做的话,我更倾向于那个,我感觉那个的速度好像比HtmlUnit快。
17 楼 buaastorm 2010-12-02  
我遇到了这么一个网页,没有button的id,这个时候不知道该怎么处理?
        <form id="f_login" name="f_login" action="" target="_self" method="post" onsubmit="return checkInput();">
        <input type="hidden" name="redirect" value="http://www.nate.com">   
        <input type="hidden" id="PASSWD_RSA" name="PASSWD_RSA" value="">
        <fieldset>
            <legend>로그인</legend>
            <dl>
                <dt>로그인</dt>
                <dd>
                    <input type="text" id="ID" name="ID" class="bg_id" maxlength="25" onclick="this.className='bg';" onKeyDown="this.className='bg';" tabindex="1" title="아이디 입력" onfocus="xXecure.showCKKeyProPopup();" onblur="xXecure.hideCKKeyProPopup();" /> @
                    <select id="domain" name="domain" tabindex="2"><!-- 2009.07 : ID 추가 -->
                        <option selected="selected">nate.com</option>
                        <option>empas.com</option>
                        <option>lycos.co.kr</option>
                        <option>netsgo.com</option>
                    </select>
                </dd>
                <dd>
                    <input type="password" id="PASSWD" name="PASSWD" class="bg_passwd" maxlength="20" onclick="this.className='bg';" onKeyDown="this.className='bg';" tabindex="3" title="비밀번호 입력" onkeypress="if (13 == event.keyCode) submit();" onfocus="xXecure.showCKKeyProPopup();" onblur="xXecure.hideCKKeyProPopup();" enc="on" />
                </dd>
                <dd class="check">
                    <input type="checkbox" id="saveid" name="saveid"  /> <label for="saveid" id="" tabindex="4">아이디 저장</label>
                    <a class="btn_otp" onclick="xXecurePop.openWin(2);" title="OTP보안 안내">OTP보안 안내</a>
                </dd>
                <dd class="btn">
                    <input type="submit" tabindex="6" title="로그인버튼" value=""/>
                </dd>
            </dl>
        </fieldset>
        </form>

底下这部分是用来提交这个form的
<input type="submit" tabindex="6" title="로그인버튼" value=""/>  
16 楼 twfy914 2010-06-08  
不知道楼上的朋友看过那种批量注册软件没?
就是什么126邮箱批量注册之内的
说白了就是把验证码直接显示到软件界面上去,然后留个输入框让你手工输入,然后手工点提交,程序自动完成其他元素的填写。明白?
15 楼 nighthawk 2010-05-17  
wxy5001 写道
Foxswily 写道
验证码是所有类似工具都要面对的问题,我尝试两种方式解决
1.图像识别,这个可以单独拿来研究了,算法难度不小,外加现在的图片干扰越来越邪乎,不好实现。
2.显示图片人工解决,个人推荐这方式。毕竟登录一次可以程序保障长期在线,性价比高的方案:)



第二个解决方法,能不能帖段代码...

1,把登陆页面刷出来。
2,肉眼识别出验证码。
3,把验证码写死到程序里去。
是不是这样
14 楼 wxy5001 2010-05-17  
Foxswily 写道
验证码是所有类似工具都要面对的问题,我尝试两种方式解决
1.图像识别,这个可以单独拿来研究了,算法难度不小,外加现在的图片干扰越来越邪乎,不好实现。
2.显示图片人工解决,个人推荐这方式。毕竟登录一次可以程序保障长期在线,性价比高的方案:)



第二个解决方法,能不能帖段代码...
13 楼 yangfuchao418 2010-04-19  
楼主写的不错。入门了。
12 楼 Foxswily 2010-04-16  
验证码是所有类似工具都要面对的问题,我尝试两种方式解决
1.图像识别,这个可以单独拿来研究了,算法难度不小,外加现在的图片干扰越来越邪乎,不好实现。
2.显示图片人工解决,个人推荐这方式。毕竟登录一次可以程序保障长期在线,性价比高的方案:)
11 楼 whaosoft 2010-04-16  
这个好使吗
10 楼 srdrm 2010-04-16  
楼上的问题提得好, 呵呵.
这个貌似一个不错的测试工具.
9 楼 xiaoyiz 2010-04-16  
验证码怎么办?
8 楼 caoyangx 2010-04-16  
我知道,谢谢。

相关推荐

Global site tag (gtag.js) - Google Analytics