精华内容
下载资源
问答
  • ![图片说明]...左边为学生表student,右边为课程表course 现在我要查询学号,姓名,选修课程名。 如何实现?其实说白了就是course字段如何实现一对多啊? 各位大神有没有见解~~
  • ![图片说明](https://img-ask.csdn.net/upload/201909/25/1569397696_834376.png)
  • Java 应用开发 课程设计报告 题 目 学生信息管理系统 指导老师 * 姓 名 * 专 业 计算机科学与技术 班 级 10 级 1 班 日 期 2012 年 6 月 目 录 Java 应用开发课程设计报告 一系统总体设计 (一)设计目标及完成功能 1...
  • 实现-超级课程表——校园登录(1)

    千次阅读 2017-09-11 10:00:20
    如果你是在校大学生,或许你用多了各种课程表,比如课程格子,超级课程表。它们都有一个共同点就是可以一键导入教务处的课程。那么一直都是用户的我们,没有考虑过它是如何实现的。那么现在就来模仿一款”超级课程表...

    如果你是在校大学生,或许你用多了各种课程表,比如课程格子,超级课程表。它们都有一个共同点就是可以一键导入教务处的课程。那么一直都是用户的我们,没有考虑过它是如何实现的。那么现在就来模仿一款”超级课程表“。

    PS:由于超级课程表是商用软件,原本提取了一些图片,但是为了避免涉及侵权问题,所有图片均已使用一张绿色圆圈代替,背景图片也以颜色代替,缺乏美观,如果你觉得太丑,可以自己寻找图片代替。

    那么说了这么久,先来看看这款高仿的软件长什么样子。本文的代码做过精简,所以界面可能有出入。

    \

    好了,界面太丑,不忍直视,先暂时忽略,本文的重点不是UI,而是如何提取课程。

    先做下准备工作。

    1. HttpWatch抓包分析工具。此工具的使用后文介绍

    2. Litepal数据持久化orm,郭大神的大作,挺好用的orm,用法详见郭霖博客。

    3. Async-android-http 数据异步请求框架,这里主要用到这个框架的异步请求以及session保持的功能,或许大多数人没有使用过这个框架的会话保持功能,反正个人觉得就是一神器,操作十分简单,就1句话,不然用HttpClient可能就没那么简单了,要自己写好多内容。具体用法参见github

    4. Jsoup网页内容解析框架,可支持jquery选择器。可以支持从本地加载html,远程加载html,支持数据抽取,数据修改等功能,如果能灵活运用这个框架,那么你想抓取什么东西都不在话下。


      既然要导入课程表,那么一定要登录教务处,结论是需要教务处的账号密码,这个好办,每个学生都有账号密码。那么怎么登录呢,这个当然不是我们人工登录了,只要提供账号密码,由程序来帮我们完成登录过程以及课程的提取过程。如果登录?首先打开教务处登录界面,打开HttpWatch进行跟踪。输入账号,密码,验证码(验证码视具体学校不同,有些学校不含验证码,有些学校含验证码,验证码的处理后文进行说明),输入完成后点击登录,再点击查看课程的菜单,之后停止HttpWatch录制,把文件保存一下进行分析。打开保存后的文件,查看登录时提交的参数及一些信息,记录下来,同时记录查看课程页提交的参数及信息。

      \

       

      先看登录页面提交的参数,参数均是POST提交,这可以通过HttpWatch看到提交方式

      __VIEWSTATE:有这个值页面生成的,这里我直接使用这个固定值而不去抓取,这个值是.net根据表单参数自动生成的。理论上同一个页面是不会变动的。

      Button1:传空值即可

      hidPdrs:传空值即可

      lbLanguage:传空值即可

      RadioButtonList1:图上是乱码,通过查看网页源代码可知该值是学生,因为我们是以学生的角色登录的

      TextBox2:这个值是密码,传密码即可

      txtSecrect:这个值是验证码,传对应的验证码即可

      txtUserName:这个值是学号,传学号即可

      你以为只要提交这些参数就好了吗,那么你就错了,我们还有设置请求头信息,如下图\

       

      我们不必设置所有请求头信息,只需要设置Host,Referer,User-Agent(可不设)。

       

      请求头设置完毕了,那么来说一个重大的问题,就是验证码的问题,这里有三种方式供选择。

      1. 在登录之前抓取验证码,显示出来,供用户输入。

      2. 使用正方的bug1,为什么是bug1呢,因为后面一种方法利用了bug2,bug1,bug2不一定所有学校适用,正方的默认登录页面是default2.aspx,如果这个页面有验证码,你可以试试default1.aspx-default6.aspx六个页面,运气好的话可能会有不需要验证码的页面。这时候你使用该页面进行登录即可(提交参数会不同,具体自己抓包分析)

      3. 使用正方的bug2,不得不说这个bug2,大概是某个程序猿在某男某月某日无意间留下的把,那么怎么使用这个bug呢,很简单,登录的时候直接传验证码为空值或者空字符串过去就好了,有人说,你他妈逗我,这都行,恩,真的行。为什么行呢,原因可能是正方后台程序没有判断传过来的值是不是空。我们模拟登录的时候并没有去请求验证码的页面,所有不会产生验证码(此时为空字符串或者空值)和cookie,当我们提交空验证码时,后台接收到的值就是空字符串,两个空字符串做比较当然相等了,以上只是猜测,毕竟正方是.net的,.net的处理机制本人不是很清楚。

         

        说了这么多理论知识,来点实际的把,先完成登录界面的代码

         

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        <relativelayout android:layout_height="match_parent" android:layout_width="match_parent" tools:context="${relativePackage}.${activityClass}" xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools">
         
             
            <imageview android:background="@drawable/icon" android:id="@+id/logo" android:layout_alignparenttop="true" android:layout_centerhorizontal="true" android:layout_height="wrap_content" android:layout_margintop="30dp" android:layout_width="wrap_content">
            <edittext android:drawableleft="@drawable/username" android:hint="教务处账号" android:id="@+id/username" android:layout_below="@id/logo" android:layout_height="wrap_content" android:layout_margintop="50dp" android:layout_width="match_parent" android:text="/">
         
            <edittext android:drawableleft="@drawable/password" android:hint="教务处密码" android:id="@+id/password" android:layout_below="@id/username" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="android:password=true">
         
            <linearlayout android:id="@+id/ll_code" android:layout_below="@id/password" android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="horizontal" android:visibility="gone">
         
                <edittext android:hint="验证码" android:id="@+id/secrectCode" android:layout_height="wrap_content" android:layout_width="100dp">
         
                <imageview android:id="@+id/codeImage" android:layout_height="36dp" android:layout_marginleft="10dp" android:layout_marginright="10dp" android:layout_width="72dp" android:scaletype="fitStart"><button android:background="@drawable/btn_login_selector" android:id="@+id/getCode" android:layout_height="40dp" android:layout_width="100dp" android:text="刷新验证码" android:textcolor="#fff"></button><button android:background="@drawable/btn_login_selector" android:id="@+id/login" android:layout_alignparentbottom="true" android:layout_below="@drawable/password" android:layout_centerhorizontal="true" android:layout_height="45dp" android:layout_marginbottom="100dp" android:layout_width="180dp" android:text="登录" android:textcolor="#fff"></button></imageview></edittext></linearlayout></edittext></edittext></imageview></relativelayout>

         

        很简单,就是账号,密码,以及验证码,这里验证码被我隐藏了,因为我使用了bug2,不需要请求验证码,对应的界面隐藏掉,但是如果你把他显示出来,获取验证码让用户输入也是可以的。

        在登录之前先初始化一下cookie,这一步必须在请求之前设置。


        ?
        1
        2
        3
        4
        5
        6
        7
        8
        /**
             * 初始化Cookie
             */
            private void initCookie(Context context) {
                //必须在请求前初始化
                cookie = new PersistentCookieStore(context);
                HttpUtil.getClient().setCookieStore(cookie);
            }

        那么HttpUtil又是什么呢,很简单,就是一个请求用的工具类

         

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        142
        143
        144
        145
        146
        147
        148
        149
        150
        151
        152
        153
        154
        155
        156
        157
        158
        159
        160
        161
        162
        163
        164
        165
        166
        167
        168
        169
        170
        171
        172
        173
        174
        175
        176
        177
        178
        179
        180
        181
        182
        183
        184
        185
        186
        187
        188
        189
        190
        191
        192
        193
        194
        195
        196
        197
        198
        199
        200
        201
        202
        203
        204
        205
        206
        package cn.lizhangqu.kb.util;
          
        import org.apache.http.Header;
          
        import android.app.ProgressDialog;
        import android.content.Context;
        import android.widget.Toast;
        import cn.lizhangqu.kb.service.LinkService;
          
        import com.loopj.android.http.AsyncHttpClient;
        import com.loopj.android.http.AsyncHttpResponseHandler;
        import com.loopj.android.http.BinaryHttpResponseHandler;
        import com.loopj.android.http.RequestParams;
          
        /**
         * Http请求工具类
         * @author lizhangqu
         * @date 2015-2-1
         */
        /**
         * @author Administrator
         *
         */
        public class HttpUtil {
            private static AsyncHttpClient client = new AsyncHttpClient(); // 实例话对象
            // Host地址
            public static final String HOST = ***.***.***.***;
            // 基础地址
            public static final String URL_BASE = https://***.***.***.***/;
            // 验证码地址
            public static final String URL_CODE = https://***.***.***.***/CheckCode.aspx;
            // 登陆地址
            public static final String URL_LOGIN = https://***.***.***.***/default2.aspx;
            // 登录成功的首页
            public static String URL_MAIN = https://***.***.***.***/xs_main.aspx?xh=XH;
            // 请求地址
            public static String URL_QUERY = https://***.***.***.***/QUERY;
          
            /**
             * 请求参数
             */
            public static String Button1 = ;
            public static String hidPdrs = ;
            public static String hidsc = ;
            public static String lbLanguage = ;
            public static String RadioButtonList1 = 学生;
            public static String __VIEWSTATE = dDwyODE2NTM0OTg7Oz7YiHv1mHkLj1OkgkF90IvNTvBrLQ==;
            public static String TextBox2 = null;
            public static String txtSecretCode = null;
            public static String txtUserName = null;
          
            // 静态初始化
            static {
                client.setTimeout(10000); // 设置链接超时,如果不设置,默认为10s
                // 设置请求头
                client.addHeader(Host, HOST);
                client.addHeader(Referer, URL_LOGIN);
                client.addHeader(User-Agent,
                        Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko);
            }
          
            /**
             * get,用一个完整url获取一个string对象
             *
             * @param urlString
             * @param res
             */
            public static void get(String urlString, AsyncHttpResponseHandler res) {
                client.get(urlString, res);
            }
          
            /**
             * get,url里面带参数
             *
             * @param urlString
             * @param params
             * @param res
             */
            public static void get(String urlString, RequestParams params,
                    AsyncHttpResponseHandler res) {
                client.get(urlString, params, res);
            }
          
            /**
             * get,下载数据使用,会返回byte数据
             *
             * @param uString
             * @param bHandler
             */
            public static void get(String uString, BinaryHttpResponseHandler bHandler) {
                client.get(uString, bHandler);
            }
          
            /**
             * post,不带参数
             *
             * @param urlString
             * @param res
             */
            public static void post(String urlString, AsyncHttpResponseHandler res) {
                client.post(urlString, res);
            }
          
            /**
             * post,带参数
             *
             * @param urlString
             * @param params
             * @param res
             */
            public static void post(String urlString, RequestParams params,
                    AsyncHttpResponseHandler res) {
                client.post(urlString, params, res);
            }
          
            /**
             * post,返回二进制数据时使用,会返回byte数据
             *
             * @param uString
             * @param bHandler
             */
            public static void post(String uString, BinaryHttpResponseHandler bHandler) {
                client.post(uString, bHandler);
            }
          
            /**
             * 返回请求客户端
             *
             * @return
             */
            public static AsyncHttpClient getClient() {
                return client;
            }
          
            /**
             * 获得登录时所需的请求参数
             *
             * @return
             */
            public static RequestParams getLoginRequestParams() {
                // 设置请求参数
                RequestParams params = new RequestParams();
                params.add(__VIEWSTATE, __VIEWSTATE);
                params.add(Button1, Button1);
                params.add(hidPdrs, hidPdrs);
                params.add(hidsc, hidsc);
                params.add(lbLanguage, lbLanguage);
                params.add(RadioButtonList1, RadioButtonList1);
                params.add(TextBox2, TextBox2);
                params.add(txtSecretCode, txtSecretCode);
                params.add(txtUserName, txtUserName);
                return params;
            }
          
            /**
             * 接口回调
             * @author lizhangqu
             *
             * 2015-2-22
             */
            public interface QueryCallback {
                public String handleResult(byte[] result);
            }
          
            /**
             * 登录后查询信息封装好的函数
             * @param context
             * @param linkService
             * @param urlName
             * @param callback
             */
            public static void getQuery(final Context context, LinkService linkService,
                    final String urlName, final QueryCallback callback) {
                final ProgressDialog dialog = CommonUtil.getProcessDialog(context,
                        正在获取 + urlName);
                dialog.show();
                String link = linkService.getLinkByName(urlName);
                if (link != null) {
                    HttpUtil.URL_QUERY = HttpUtil.URL_QUERY.replace(QUERY, link);
                } else {
                    Toast.makeText(context, 链接出现错误, Toast.LENGTH_SHORT).show();
                    return;
                }
                HttpUtil.getClient().addHeader(Referer, HttpUtil.URL_MAIN);
                HttpUtil.getClient().setURLEncodingEnabled(true);
                HttpUtil.get(HttpUtil.URL_QUERY, new AsyncHttpResponseHandler() {
                    @Override
                    public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
                        if (callback != null) {
                            callback.handleResult(arg2);
                        }
                        Toast.makeText(context, urlName + 获取成功!!!, Toast.LENGTH_LONG)
                                .show();
                        dialog.dismiss();
                    }
          
                    @Override
                    public void onFailure(int arg0, Header[] arg1, byte[] arg2,
                            Throwable arg3) {
                        dialog.dismiss();
                        Toast.makeText(context, urlName + 获取失败!!!, Toast.LENGTH_SHORT)
                                .show();
                    }
                });
            }
        }

         

        地址信息被我处理掉了,替换成对应的地址即可,都是几个简单的函数,其中最后一个函数做了一个封装,代码自己读吧,这里就不讲了。。。。。

        现在查看登录的代码。

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        /**
             * 登录
             */
            private void login() {
                HttpUtil.txtUserName = username.getText().toString().trim();
                HttpUtil.TextBox2 = password.getText().toString().trim();
                //需要时打开验证码注释
                //HttpUtil.txtSecretCode = secrectCode.getText().toString().trim();
                if (TextUtils.isEmpty(HttpUtil.txtUserName)
                        || TextUtils.isEmpty(HttpUtil.TextBox2)) {
                    Toast.makeText(getApplicationContext(), 账号或者密码不能为空!,
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                final ProgressDialog dialog =CommonUtil.getProcessDialog(LoginActivity.this,正在登录中!!!);
                dialog.show();
                RequestParams params = HttpUtil.getLoginRequestParams();// 获得请求参数
                HttpUtil.URL_MAIN = HttpUtil.URL_MAIN.replace(XH,
                        HttpUtil.txtUserName);// 获得请求地址
                HttpUtil.getClient().setURLEncodingEnabled(true);
                HttpUtil.post(HttpUtil.URL_LOGIN, params,
                        new AsyncHttpResponseHandler() {
          
                            @Override
                            public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
                                try {
                                    String resultContent = new String(arg2, gb2312);
                                    if(linkService.isLogin(resultContent)!=null){
                                        String ret = linkService.parseMenu(resultContent);
                                        Log.d(TAG, login success:+ret);
                                        Toast.makeText(getApplicationContext(),
                                                登录成功!!!, Toast.LENGTH_SHORT).show();
                                        jump2Main();
          
                                    }else{
                                        Toast.makeText(getApplicationContext(),账号或者密码错误!!!, Toast.LENGTH_SHORT).show();
                                    }
          
                                } catch (UnsupportedEncodingException e) {
                                    e.printStackTrace();
                                } finally {
                                    dialog.dismiss();
                                }
                            }
                            @Override
                            public void onFailure(int arg0, Header[] arg1, byte[] arg2,
                                    Throwable arg3) {
                                Toast.makeText(getApplicationContext(), 登录失败!!!!,
                                        Toast.LENGTH_SHORT).show();
                                dialog.dismiss();
                            }
                        });
            }
        通过抓取关键字,判断是否登录成功,登录成功则解析菜单,对应的逻辑被我封装在service层里了
        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        package cn.lizhangqu.kb.service;