博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 模拟登陆网站实现移动客户端
阅读量:5745 次
发布时间:2019-06-18

本文共 12705 字,大约阅读时间需要 42 分钟。

你有没有想过自己来为一个网站做一个手机客户端呢?

想要设计一个客户端,一般来说都需要实现模拟登陆功能,这样才能获取用户的个人信息,不然都直接通过手机浏览器网页来访问的话,效果不好且界面不友好

这里来模拟登陆我学校的图书馆,平台为安卓系统

一、准备工具

需要用到的工具库有两个:

  1. Jsoup
    jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据

2.android-async-http

android-async-http是一个强大的网络请求库,这个网络请求库是基于Apache HttpClient库之上的一个异步网络请求处理库,网络处理均基于Android的非UI线程,通过回调方法处理请求结果

下载然后将之导入工程中

img_80855100515fa318c15510f4fe0f7706.png
这里写图片描述

二、了解思路

我学校的图书馆网址是:

img_4950ff06723a1954ef88689c8831540d.png
这里写图片描述

个人账号登录网址是:

img_81c26fb10b9e9088aec92c0440166245.png
这里写图片描述

正常来说我应该是在本界面登录的,输入学号、密码、验证码等信息,不过鼓捣了一下网站,发现了网站有个隐藏的登录页面:

根据网址名可以判断出该页面应该是在开发网站时用来测试的

img_42e496ebdac79331cd01a2424b0c276c.png
这里写图片描述

而这个页面居然不需要验证码=_=!

这样就省事很多了

现在就开始来研究下如何实现模拟登录,看看需要向服务器发送什么信息

用谷歌浏览器打开登录页,按F12键,点击Application标签,查看Cookie

img_90de99ae9b1aeaf0043ec4cf6b0aea75.png
这里写图片描述

图片箭头所指向的值即为当前用户的Cookie值,每次打开该页面,该值应该都是不同的,即用来唯一标示每位用户

转到Network标签,查看访问信息

箭头所指即为请求头,后边需要用到
可以看到请求头中的一项为Cookie

img_0d57d08ac2c41a7b3ff8ba173b71ff6d.png
这里写图片描述

点击右键查看网页源代码,删去一些无用的代码,重点在于中间的表单form

图书证号:
密        码:

form标签中的action="test.aspx"意思是提交的数据需要Post给谁,这里是直接Post到本页面即可

而每一个input标签都是需要提交的数据,name值代表数据名,value即数据值
例如当登录时,提交的所有信息中就有名为“txtUsername_Lib”,值为学号的数据

模拟登录的过程简单来说,即用户首先访问登录页,获得了Cookie值,然后输入数据将每一项input数据Post给服务器,如果登录成功,则之后访问个人信息页面只需要带上Cookie值即可,因为此时服务器已经知道该Cookie对应哪位用户了

三、敲代码

现在就来正式敲代码实现模拟登陆了

为了简化网络请求操作,我简单封装了Get和Post操作

/** * Get和Post操作的简单封装 * Created by ZY on 2016/10/29. */public class NetAPI {    /**     * Get操作     *     * @param client   client     * @param url      url     * @param charset  编码格式     * @param callback 回调函数     */    public static void HttpGet(AsyncHttpClient client, String url, String charset, final NetCallback callback) {        client.get(url, new TextHttpResponseHandler(charset) {            @Override            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {                if (callback != null) {                    callback.onFailure("状态码:" + statusCode);                }            }            @Override            public void onSuccess(int statusCode, Header[] headers, String responseString) {                if (callback == null) {                    return;                }                if (statusCode == HttpStatus.SC_OK) {                    callback.onSuccess(headers, responseString);                } else {                    callback.onFailure("");                }            }        });    }    /**     * Post操作     *     * @param client   client     * @param url      url     * @param params   请求参数     * @param charset  编码格式     * @param callback 回调函数     */    public static void HttpPost(AsyncHttpClient client, String url, RequestParams params, String charset, final NetCallback callback) {        client.post(url, params, new TextHttpResponseHandler(charset) {            @Override            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {                if (callback != null) {                    callback.onFailure("状态码:" + statusCode);                }            }            @Override            public void onSuccess(int statusCode, Header[] headers, String responseString) {                if (callback == null) {                    return;                }                if (statusCode == HttpStatus.SC_OK) {                    callback.onSuccess(headers, responseString);                } else {                    callback.onFailure("");                }            }        });    }}

用到的回调函数

/** * 回调函数 * Created by ZY on 2016/10/29. */public interface NetCallback {    void onFailure(String response);    void onSuccess(Header[] headers, String response);}

新建一个LibraryAPI类,采用单例模式

//图书馆登录    private final static String LIBRARY_LOGIN_URL = "http://lib.wyu.edu.cn/opac/test.aspx";    //图书馆个人信息    private final static String USER_INFO_URL = "http://lib.wyu.edu.cn/opac/user/userinfo.aspx";    //当前借书    private final static String BOOK_BORROWED_URL = "http://lib.wyu.edu.cn/opac/user/bookborrowed.aspx";    //借书历史    private final static String BOOK_BORROWED_HISTORY_URL = "http://lib.wyu.edu.cn/opac/user/bookborrowedhistory.aspx?page=";    private AsyncHttpClient client;    private static LibraryAPI libraryAPI;    //私有化构造函数    private LibraryAPI(Context context) {        client = new AsyncHttpClient();        PersistentCookieStore cookieStore = new PersistentCookieStore(context);        cookieStore.clear();        client.setCookieStore(cookieStore);        //设置请求头        client.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");        client.addHeader("Accept-Encoding", "gzip, deflate, sdch");        client.addHeader("Accept-Language", "zh-CN,zh;q=0.8");        client.addHeader("Cache-Control", "max-age=0");        client.addHeader("Connection", "Keep-Alive");        client.addHeader("Host", "lib.wyu.edu.cn");        client.addHeader("Referer", "http://lib.wyu.edu.cn/opac/search.aspx");        client.addHeader("Upgrade-Insecure-Requests", "1");        client.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36");    }    /**     * 获取实例     *     * @param context 上下文     * @return 实例     */    public static LibraryAPI getInstance(Context context) {        if (libraryAPI == null) {            libraryAPI = new LibraryAPI(context);        }        return libraryAPI;    }

当中的请求头根据谷歌浏览器中查看的数据来设置即可,有些请求头不是必须的,不过为了省事就全带上了

需要注意的是以下一步:

PersistentCookieStore cookieStore = new PersistentCookieStore(context);        cookieStore.clear();        client.setCookieStore(cookieStore);

即将Cooki保存到本地,即Cookie持久化,每次重新调用时清理本地Cookie

之后就是来登录图书馆了

/***     * 登录图书馆     *     * @param studentID 学号     * @param password  密码     * @param callback  回调函数     */    public void login(String studentID, String password, final NetCallback callback) {        //添加请求参数        final RequestParams params = new RequestParams();        params.add("__EVENTTARGET", "");        params.add("__EVENTARGUMENT", "");        params.add("__VIEWSTATE", "/wEPDwUKLTY2Njg2ODg0Mw9kFgICAw8WAh4GdGFyZ2V0BQZfYmxhbmsWBAIDD2QWBGYPD2QWAh4MYXV0b2NvbXBsZXRlBQNvZmZkAgQPDxYCHgRUZXh0ZWRkAgUPZBYGZg8QZGQWAWZkAgEPEGRkFgFmZAICDw9kFgIfAQUDb2ZmZGTcY8B98vBh8r3/5k/FWW0LQrvmCw==");        params.add("txtlogintype", "0");        params.add("btnLogin_Lib", "登录");        params.add("__EVENTVALIDATION", "/wEWBQK2h7H9DgLxkMjADwLfkqekBgLN05+fBgKe9OnfBhxoxkpnmMwQ62JlcaByWkgdRCZp");        params.add("txtUsername_Lib", studentID);        params.add("txtPas_Lib", password);        NetAPI.HttpGet(client, LIBRARY_LOGIN_URL, "UTF-8", new NetCallback() {            @Override            public void onFailure(String response) {                callback.onFailure(response + " 获取图书馆登录页失败");            }            @Override            public void onSuccess(Header[] headers, String response) {                NetAPI.HttpPost(client, LIBRARY_LOGIN_URL, params, "UTF-8", new NetCallback() {                    @Override                    public void onFailure(String response) {                        callback.onFailure(response + " 登录图书馆失败");                    }                    @Override                    public void onSuccess(Header[] headers, String response) {                        for (Header header : headers) {                            //Post后返回的headers中必须含有该header,才证明Post成功                            if (header.getName().equals("Set-Cookie")) {                                callback.onSuccess(headers, response);                                return;                            }                        }                        callback.onFailure("登录图书馆失败");                    }                });            }        });    }

首先需要以Get方式访问登录页,此时Cookie值会被自动保存下来,Get成功后就可以来向登录页Post学号、密码等数据了,这些数据都保存在请求参数RequestParams当中

有些请求参数的名与值都是不变了,我也不知道Post到服务器到底有什么用处~~

此时,即使Post成功了,也不代表就登录成功了,用谷歌浏览器来查看,可以发现当登录成功后,返回的Header[]当中会带上一个名为“Set-Cookie”的数据

所以检查返回的Header[]即可知道是否已经登录成功

如果登录成功,就可以调用以下方法获取当前借阅书籍列表了

/**     * 获取当前借阅情况     *     * @param callback 回调函数     */    public void getBookBorrowed(NetCallback callback) {        NetAPI.HttpGet(client, BOOK_BORROWED_URL, "UTF-8", callback);    }

为了方便,新建一个书籍实体Book

/** * 在查询当前借书情况与借书历史时使用 * Created by ZY on 2016/10/29. */public class Book {    /**     * 书名     */    private String bookName;    /**     * 登录号     */    private String id;    /**     * 借书日期     */    private String borrowDate;    /**     * 书籍最迟应还日期/书籍还期     */    private String deadline;    public Book(String bookName, String id, String borrowDate, String deadline) {        this.bookName = bookName;        this.id = id;        this.borrowDate = borrowDate;        this.deadline = deadline;    }    public String getBookName() {        return bookName;    }    public String getId() {        return id;    }    public String getBorrowDate() {        return borrowDate;    }    public String getDeadline() {        return deadline;    }    @Override    public String toString() {        return "Book{" +                "bookName='" + bookName + '\'' +                ", id='" + id + '\'' +                ", borrowDate='" + borrowDate + '\'' +                ", deadline='" + deadline + '\'' +                '}' + "\n";    }}

现在即可来获取书籍借阅列表了,将获取到的数据显示在TextView上

private void getBookList() {        final LibraryAPI libraryAPI = LibraryAPI.getInstance(this);        libraryAPI.login("填入学号", "填入密码", new NetCallback() {            @Override            public void onFailure(String response) {                tv_content.setText("登录失败");            }            @Override            public void onSuccess(Header[] headers, String response) {                libraryAPI.getBookBorrowed(new NetCallback() {                    @Override                    public void onFailure(String response) {                        tv_content.setText("获取当前书籍借阅情况失败");                    }                    @Override                    public void onSuccess(Header[] headers, String response) {                        List
bookList = HtmlParseHelper.parseBookBorrowed(response); if (bookList == null) { tv_content.setText("解析当前书籍借阅情况失败"); return; } if (bookList.size() == 0) { tv_content.setText("当前木有借书"); return; } StringBuilder builder = new StringBuilder(); for (Book book : bookList) { builder.append(book.toString()); } tv_content.setText(builder.toString()); System.out.println(builder.toString()); } }); } }); }

当中需要用到一个工具类HtmlParseHelper,因为服务器返回的是Html代码,需要将之解析为格式友好的数据

/**     * 解析当前书籍借阅情况     *     * @param html html文件     * @return 书籍列表     */    public static List
parseBookBorrowed(String html) { List
bookList; try { Document document = Jsoup.parse(html); Element divElement = document.select("div#borrowedcontent").first(); Element tbodyElement = divElement.getElementsByTag("tbody").first(); Elements trElements = tbodyElement.getElementsByTag("tr"); bookList = new ArrayList<>(); Elements tdElements; Book book; String bookName; String id; String borrowDate; String deadline; for (Element trElem : trElements) { tdElements = trElem.getElementsByTag("td"); bookName = tdElements.get(2).text(); id = tdElements.get(5).text(); borrowDate = tdElements.get(6).text(); deadline = tdElements.get(1).text(); book = new Book(bookName, id, borrowDate, deadline); bookList.add(book); } } catch (Exception e) { return null; } return bookList; }

获取到的数据如下:

img_2b8c8ea94d57912de9d1d5f316af57f0.png
这里写图片描述

数据没错,的确是我当前借的书

按照这方法,就可以获取到所有的个人信息,再设计好看点的UI界面,就可以完成一个图书馆客户端了~

四、补充

可能有人会说没有验证码的登录页面毕竟是少数,有验证码的页面又该如何操作?

其实即使有验证码,登录操作也就是麻烦了点,也并不难

这里再以我学校的学生服务子系统为例子,网址:

按F12查看Cookie,可以看到当中有一项名为“LogonNumber”的数据,值即为图片验证码当中的数字

img_4584bbab9485bccc0eb6573fd9b7ce41.png
这里写图片描述

这样只要先遍历Cookie值,取出验证码值,在Post时将之加入请求参数即可,也不需要用户来输入验证码值了,简化了操作

如果这样不行的话,也可以查看源代码获取验证码链接,在登录时下载该图片并显示,由用户输入验证码即可

方法有很多,只要多钻研下,总归是能够实现的

代码我已上传到GitHub上:

转载地址:http://nlazx.baihongyu.com/

你可能感兴趣的文章
浅谈JavaScript、ES5、ES6
查看>>
Codeforces Round #327 (Div. 2) C. Median Smoothing 找规律
查看>>
ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded
查看>>
C#集合u
查看>>
最少拦截系统
查看>>
lintcode :Segmemt Tree Build II
查看>>
Spring、Hello AOP
查看>>
SAP 金税接口代码 供参考
查看>>
类的方法类型——构造方法
查看>>
【转】关于onActivityResult方法不执行的问题汇总
查看>>
ubuntu防火墙命令初探
查看>>
数据结构——算法之(032)(求两个串中的第一个最长子串)
查看>>
javascript Dictionary data structures
查看>>
BZOJ 1449 JSOI2009 球队收益 费用流
查看>>
html有序列表和无序列表
查看>>
uva 1510 - Neon Sign(计数)
查看>>
微信的redirect_uri参数错误解决办法
查看>>
(转)ashx 使用Session
查看>>
C#日期格式化
查看>>
MarkdownPad2.5 注册码
查看>>