• Android登录client,验证码的获取,网页数据抓取与解析,HttpWatch基本使用


    大家好,我是M1ko。在互联网时代的今天,假设一个App不接入互联网。那么这个App一定不会有长时间的生命周期,因此Android网络编程是每个Android开发人员必备的技能。博主是在校大学生,自学Android一年半多。正好通过一个模拟登录校园网软件,来给大家演示怎样在网页上抓取我们想要的数据,以及将数据Post给server。

    假设有什么错误或改进欢迎大家指正=-= ,假设想交流博主qq 136057505

    好的废话不多说看一下我们的重点

    一、Http基础

    有Http基础的朋友都知道,我们是通过Get 与Post请求与server进行交互的。Get顾名思义就是获取信息。Post就是想server发请求,可是Post也能够用来获取信息而且比Get有非常多优势,我们这里就是使用的Post。

    Java中有非常多方式与server进行连接,常见的有HttpUrlCollection,HttpClient。两者的优缺点:

    HttpUrlCollection长处:
    - 轻量,拓展性强
    - 节省资源
    缺点:
    - 代码量大复杂

    HttpClient长处:
    - 便捷。代码简单
    缺点:
    -拓展性不足。耗费资源多

    实际上在Android6.0中Google已经删除了HttpClient的API官方理由是耗电量大,可是HttpCollection的代码量实在是大。因此我们还是使用了HttpClient,仅仅需在build.gradle中加入以下语句
    android {
    useLibrary 'org.apache.http.legacy'
    }
    就可以。

    二、server地址的获取 ##

    我们要向serverPost数据要知道Post的地址是谁,这就要用我们的抓包软件了。博主使用的是HttpWatch与火狐的FireBug,两个软件的功能相互补充。两个软件主要的用法大家自行Google(百度,嘿嘿嘿=-=)
    1.验证码的地址
    ok首先打开我们的校园网登录网页:
    http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/Default2.aspx
    我们点击IE 中HttpWatch Recordbutton,然后刷新这个站点我们能够看到例如以下的界面
    非常多的Get请求
    我们能够看到非常多Get请求这些Get请求就能够获取打开的界面的各个元素。右键G右方的网址,选择Open in the new Tab就能够看到各个元素,细心(累死=-=)的查找之后我们发现验证码的网址是
    http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/CheckCode.aspx 打开后例如以下图显示
    验证码的图片

    2.登陆post地址
    获得验证码的地址之后。我们还要获得登陆server的地址,还是使用HttpWatch。我们输入进去账号password以及验证码之后,单击登录開始抓取例如以下图所看到的
    打开以下的POST DATA就能够看到我们发送的数据以及他们的类别
    __VIEWSTATE
    Button1
    hidPdrs
    hidsc
    lbLanguag
    RadioButtonLi
    TextBox2 password
    txtSecretCode 验证码
    txtUserName username 这里不给username,有须要的QQ 加我136057505=-=
    这些值非常关键,我们在这里面能够找到相应的值,假设没有值那就是空,好的这些我们以下会具体讲,我们还没有获得Post地址,右键Post这一行Copy就可以。ok这样我们发送Post请求的地址就到手了。


    http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/Default2.aspx

    三、登陆

    1.验证码图片的获取
    我们有了各种地址,以下就要登陆了。可是我们知道了账号和password(嘿嘿嘿。就不告诉你们=-=),我们还要知道验证码,怎么获得图片呢?上文中我们已经知道了验证码的地址了。我们能够Get请求,但之前也说过一般使用Post请求来进行。于是我们上代码

      HttpPost httPost = new HttpPost(VERIFATIONURL);
                    HttpClient client = new DefaultHttpClient();
                    try {
                        HttpResponse httpResponse = client.execute(httPost);
                        byte[] bytes = new byte[1024];
                        bytes = EntityUtils.toByteArray(httpResponse.getEntity());
                        bmVerifation = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

    HttpClient的具体用法大家自行Google这里不具体展开
    VERIFATIONURL就是上文中的验证码地址,首先创建post请求,再创建一个Httpclient ,然后是HttpResponse即响应,我们获取的server的返回值就在通过他获取,我们创建一个byte数组。首先将响应中获得的数据转化为byte数组,然后通过 BitmapFactory.decodeByteArray(bytes, 0, bytes.length);方法将byte数组编译为bitmap,这时候我们就获得了验证码的Bitmap了,我们能直接ImageView.SetBitmap么?当然不能。首先第一点我们在网页上获取图片是一个耗时操作,所以我们不能在主线程中进行,第二点仅仅有在主线程中才干更新UI因此。我们使用Thread+Handler解决方法。上代码:

    private void DoGetVerifation() {
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    HttpPost httPost = new HttpPost(VERIFATIONURL);
                    HttpClient client = new DefaultHttpClient();
                    try {
                        HttpResponse httpResponse = client.execute(httPost);
                        byte[] bytes = new byte[1024];
                        bytes = EntityUtils.toByteArray(httpResponse.getEntity());
                        bmVerifation = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    Message msg = new Message();
                    msg.arg1 = 10;
                    handler.sendMessage(msg);
                }
            }).start();
        }

    主线程中接收到msg之后ivVerifation.setImageBitmap(bmVerifation);这样我们就得到了验证码的图片而且在主界面上显示出来了

    2.发送Post请求登录

    我们眼下知道了username,password。验证码,下一步就是要登录了,登陆相同是个耗时操作。当然也要开启一个新的线程。这没什么好说的了,我们相同是发送Post请求,上代码:

    private void DoLogin(final String user, final String password, final String verifation) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    DefaultHttpClient defaultclient = new DefaultHttpClient();
                    HttpPost httpPost = new HttpPost(LOGINURL);
                    HttpResponse httpResponse;
    
                    //设置post參数
                    List<NameValuePair> params = new ArrayList<NameValuePair>();
                    params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
                    params.add(new BasicNameValuePair("Button1", ""));
                    params.add(new BasicNameValuePair("hidPdrs", ""));
                    params.add(new BasicNameValuePair("hidsc", ""));
                    params.add(new BasicNameValuePair("lbLanguage", ""));
                    params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
                    params.add(new BasicNameValuePair("TextBox2", password));
                    params.add(new BasicNameValuePair("txtSecretCode", verifation));
                    params.add(new BasicNameValuePair("txtUserName", user));
    
                    //获得个人主界面的HTML
                    try {
                        httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
                        httpResponse = defaultclient.execute(httpPost);
                        Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
    
                        if (httpResponse.getStatusLine().getStatusCode() == 200) {
                            StringBuffer sb = new StringBuffer();
                            HttpEntity entity = httpResponse.getEntity();
                            MAINBODYHTML = EntityUtils.toString(entity);
                            IsLoginSuccessful(MAINBODYHTML);
                        }
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    } catch (ClientProtocolException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    

    首先是创建post,client response,与前面无异,我们获取数据的Post请求,比方说获取验证码请求是不须要參数的,可是我们登录须要发送给server username password 验证码,于是我们为Post请求设置參数

     //设置post參数
                    List<NameValuePair> params = new ArrayList<NameValuePair>();
                    params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
                    params.add(new BasicNameValuePair("Button1", ""));
                    params.add(new BasicNameValuePair("hidPdrs", ""));
                    params.add(new BasicNameValuePair("hidsc", ""));
                    params.add(new BasicNameValuePair("lbLanguage", ""));
                    params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
                    params.add(new BasicNameValuePair("TextBox2", password));
                    params.add(new BasicNameValuePair("txtSecretCode", verifation));
                    params.add(new BasicNameValuePair("txtUserName", user));
                        //获得个人主界面的HTML
                    try {
                        httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
                        httpResponse = defaultclient.execute(httpPost);
                        Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
    
                        if (httpResponse.getStatusLine().getStatusCode() == 200) {
                            StringBuffer sb = new StringBuffer();
                            HttpEntity entity = httpResponse.getEntity();
                            MAINBODYHTML = EntityUtils.toString(entity);
                            IsLoginSuccessful(MAINBODYHTML);
                        }
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    } catch (ClientProtocolException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
    

    这些參数就是我们之前使用HttpWatch获取的Post data 仅仅有类型没有值的參数我们设置为“”空,使用httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));方法将我们设置的param 參数集合给httppost,以下和之前一样,也是通过Response获取响应值,仅仅只是这里返回的不是图片而是赤果果的String=-=,IsLoginSuccessful这个函数是用来推断我们是否登录成功。这就要用到我们以下要说的数据解析了。

    四、数据解析

    1.返回的响应?
    我们登陆后会得到server给我们的响应。我们在火狐的Firebug(HttpWatch也能够个人感觉看响应值Firebug比較方便)上机型一次成功的登陆。看一下他到底会给我们返回什么
    如图我们能够知道实际上响应值就是我们的网页的HTML。假设登陆成功实际上就是返回个人首页
    这里写图片描写叙述
    那么假设失败呢?我们模拟一次验证码错误例如以下图。可见相同返回一个HTML
    这里写图片描写叙述
    我们注意这一行

    <script language='javascript' defer>alert('验证码不对!。');document.getElementById('TextBox2').focus();
    </script>

    这实际上是弹出一个窗体,提示我们验证码错误。以下我们就会利用里面不同的提示来推断我们登陆的状态

    2.使用Jsoup进行数据解析
    我们创建之前提过的IsLoginSuccessful函数

       private void IsLoginSuccessful(String loginresult) {
            Document doc = Jsoup.parse(loginresult);
            Elements alert = doc.select("script[language]");
            Elements success = doc.select("a[href]");
    
            Message msg = new Message();
            //先推断是否登录成功。若成功直接退出
            for (Element link : success) {
                //获取所要查询的URL,这里相应地址button的名字叫成绩查询
                if (link.text().equals("等级考试查询")) {
                    Log.i("xyz", "登录成功");
                    msg.arg1 = 6;
                    handler.sendMessage(msg);
                    return;
                }
            }
    
            for (Element link : alert) {
                //刷新验证码
                DoGetVerifation();
                //获取错误信息
                if (link.data().contains("验证码不对")) {
                    Log.i("xyz", "验证码错误");
                    msg.arg1 = 0;
                    handler.sendMessage(msg);
                } else if (link.data().contains("username不能为空")) {
                    Log.i("xyz", "username不能为空");
                    msg.arg1 = 1;
                    handler.sendMessage(msg);
                } else if (link.data().contains("password错误")) {
                    Log.i("xyz", "password或username错误");
                    msg.arg1 = 2;
                    handler.sendMessage(msg);
                } else if (link.data().contains("password不能为空")) {
                    Log.i("xyz", "password不能为空");
                    msg.arg1 = 3;
                    handler.sendMessage(msg);
               ......
            }
        }
    

    我使用的是一个开源的库Jsoup进行解析当然还有非常多方法,大家自行Google(百度=-=咋老躺枪)
    http://www.open-open.com/jsoup/ Jsoup的中文API指南大家能够看一下
    首先我们推断登陆成功,我们在登陆成功的HTML中找到了例如以下语句

    <a href="xsdjkscx.aspx?xh=2014117170&xm=邹德宏&gnmkdm=N121606" target='zhuti' onclick="GetMc('等级考试查询');">
    等级考试查询</a>

    我们通过 Elements success = doc.select(“a[href]”);获取到a href”开头的数据 然后我们循环推断是否存在等级考试查询这一项,假设存在。那肯定返回的响应就是我们的个人首页了。也就是说登陆成功,我们使用Thread+Handler在主线程中Toast提醒用户
    然后我们通过 Elements alert = doc.select(“script[language]”);获取到script 开头的语句,相同循环推断,这里要注意的是,与a href不同的是。我们要用的是link.data(),前面用的是link.text();
    JsoupAPI 这样写的

    • text()获取文本内容text(String value) 设置文本内容
    • data()获取数据内容(比如:script和style标签)
      ok我们通过不同的模拟操作抓取各种错误。这里不详写

    五、获取个人信息

    我们登录到了个人首页,我们想进一步获取自己的信息比方说四六级成绩,怎么办呢?相同还是抓取请求
    这里写图片描写叙述
    跟获取验证码网址的方法一样。我们得到了成绩表的网址
    http://jwxt.nwu.edu.cn/(dlxrmg55j21wlaqv2z5rcdyi)/xsdjkscx.aspx?xh=2014117170&xm=邹德宏&gnmkdm=N121606
    前面http://jwxt.nwu.edu.cn/(dlxrmg55j21wlaqv2z5rcdyi)/是我们訪问的host是不变的。我们要做的就是获取xsdjkscx.aspx?

    xh=2014117170&xm=邹德宏&gnmkdm=N121606。这个问题困扰了非常长时间,后来猛然发现这万至居然在前面说的deng’lu’cheng’登陆成功后返回的HTML中。所以还是使用Jsoup进行分析

     Document doc = Jsoup.parse(MAINBODYHTML);
            Elements links = doc.select("a[href]");
            StringBuffer sb = new StringBuffer();
            for (Element link : links) {
                //获取所要查询的URL,这里相应地址button的名字叫成绩查询
                if (link.text().equals("等级考试查询")) {
                    sb.append(link.attr("href"));
                }
            }
            GETSCOREURL = sb.toString();

    GETSCOREURL就是我们要的后半个地址,然后我们加上前半个,进行Post请求抓取就可以
    这里返回的是一个Html的表格

    <table class="datelist" cellspacing="0" cellpadding="3" border="0" id="DataGrid1" width="100%"
    >
        <tr class="datelisthead">
            <td>学年</td><td>学期</td><td>等级考试名称</td><td>准考证号</td><td>考试日期</td><td>成绩</td><td>听力成绩</td><td>阅读成绩</td
    ><td>写作成绩</td><td>综合成绩</td>
        </tr><tr>
            <td>2014-2015</td><td>2</td><td>CET4</td><td>610041151112625</td><td>201506</td><td>483</td><td>167
    </td><td>173</td><td>143</td><td>0</td>
        </tr><tr class="alt">
            <td>2015-2016</td><td>1</td><td>CET6</td><td>610041152209503</td><td>201512</td><td>376</td><td>126
    </td><td>143</td><td>107</td><td>0</td>
        </tr>
    </table>

    相同使用Jsoup

     private void parse(String parse) {
    
            Document doc = Jsoup.parse(parse);
            Elements trs = doc.select("table").select("tr");
            for (int i = 0; i < trs.size(); i++) {
                Elements tds = trs.get(i).select("td");
                for (int j = 0; j < tds.size(); j++) {
                    String text = tds.get(j).text();
                    score[i][j] = text;
                    Log.i("xyz", score[i][j]);
                }
            }
        }

    解析完我们就得到成绩的数组了

    六、总结

    博主大二党自学Android开发1年半了,如今最终有入门的感觉了,这篇教程对新手来说坑能会有些难。可是一定要沉得住气,博主刚下载HttpWatch那一会都懵逼了。啥都不懂,慢慢摸索之后最终有了头绪,好了就这么些,邮箱交流的朋友加我QQ 136057505=-=

  • 相关阅读:
    Unity shader之金属质感衣服
    Unity之如何使用夜神模拟器logcat
    Unity XLua之协程
    Unity shader之ColorMask
    NGUI之实现连连看小游戏
    NGUI之使用UISprite画线
    Unity如何退出游戏
    c#之AES加密解密
    Unity shader学习之屏幕后期处理效果之高度雾,重建world pos方法2
    Unity shader学习之屏幕后期处理效果之高度雾,重建world pos方法1
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7363520.html
Copyright © 2020-2023  润新知