博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android实现模拟登陆教务系统并解析网页数据
阅读量:6786 次
发布时间:2019-06-26

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

版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/u013132758。 https://blog.csdn.net/u013132758/article/details/73203820

前言

     时光飞逝,日月如梭,转眼间四年的大学生活已经结束啦!开始了程序员的加班生活。我的第二学位的毕业设计是实现一个学习小助手。这其中最重要的环节就是模拟登录学校的教务系统,获取到教务系统的数据并解析,用自己的数据库存储,展示在自己设计的界面上。例如课程表我是仿照超级课程表的界面来设计的。废话不多说下面先看看效果。

   

        

抓取教务系统登录时需要传递的参数

     模拟登录之前我们首先需要抓取我们登录时所需要传递的数据,可能有人会想用专业的抓包工具来抓包,其实不用那么复杂,用火狐浏览器或者Chrom浏览器的开发者模式就可以了。博主用的是火狐浏览器抓包,传的一些参数也是按照火狐浏览器来的。

首先,我们来看一下模拟器登录时需要传递的参数及HTTP请求头,我们可以看到请求是post请求,请求头字段基本差不多

传递的参数有这么些

其中这些参数中lt这个是搞的我最头疼的。本以为lt是一个固定值,但是模拟登录的时候发现不对,每次抓包lt是不一样的。最后,只能通过学习JS代码,研究这个lt的取值,最后发现lt及其他参数都隐藏在返回的HTML页面的代码中:(如下图)

搞清楚需要传什么参数和请求的HTTP头了,我们就可以模拟登录教务系统了。

自定义HTTPClient实现模拟登录

实现模拟登录首先要自定义HTTPClient,来模拟浏览器发送HTTP请求。自定义HTTP如下代码:

/** * Http请求工具类 * @author leiqi * @date 2017-3-4 */public class HttpUtil {	private static AsyncHttpClient client = new AsyncHttpClient(); // 实例话对象	// Host地址	public static final String HOST = "cas.hdu.edu.cn";	// 基础地址	public static final String URL_BASE = "http://i.hdu.edu.cn/dcp/xphone/";	// 验证码地址	public static final String URL_CODE = "http://cas.hdu.edu.cn/cas/Captcha.jpg";	// 登陆地址	public static final String URL_LOGIN = "http://cas.hdu.edu.cn/cas/login?service=http%3a%2f%2fi.hdu.edu.cn%2fdcp%2fxphone%2fm.jsp";	// 登录成功的首页	public static String URL_MAIN = "http://i.hdu.edu.cn/dcp/xphone/m.jsp";	//当前学期课表	public static String URL_Course= "http://i.hdu.edu.cn/dcp/xphone/kbcx0.jsp";	//考试安排url	public static String URl_KSAP = "http://i.hdu.edu.cn/dcp/xphone/ksap.jsp";	//校园新闻	public static String Url_XYXW = "http://m.hdu.edu.cn/";	//上课时间	public static String Url_Time = URL_BASE+"TimeTable.jsp";	//我的学费	public static String Url_WDXF = "http://yxt.hdu.edu.cn/EducationManager/xphone/m.jsp";	/**	 * 请求参数	 */	public static String captcha = "";//验证码	public static String encodedService = "http%3a%2f%2fi.hdu.edu.cn%2fdcp%2fxphone%2fm.jsp";	public static String loginErrCnt = "0";	public static String lt = "LT-1552500-7P9jMewfE3wH2dWObBuJ";	public static String password = "19191cf09b99b03ab0df1db04c3840ed";	public static String username = null;	public static String service = "http://i.hdu.edu.cn/dcp/xphone/m.jsp";	public static String serviceName = null;	public static String ticket = null;	public static String Cookie = "key_dcp_cas=HHxRYyDMhr0GmDfQ6vgPxXQT8yCsvPSCg0MWBnnnWMGv4dQngpLS!748587538; route=4376efc7edf61c9fe699e82a2fb7a34f" ;	// 静态初始化	static {		client.setTimeout(10000); // 设置链接超时,如果不设置,默认为10s		// 设置请求头		client.addHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");		client.addHeader("Accept-Encoding","gzip, deflate");		client.addHeader("Accept-Language","zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");		client.addHeader("Connection","keep-alive");		client.addHeader("Cookie", Cookie);		client.addHeader("Host", HOST);		client.addHeader("Referer", URL_LOGIN);		client.addHeader("User-Agent",				"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Firefox/52.0");		client.addHeader("Upgrade-Insecure-Requests","1");	}	/**	 * 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,不带参数,获取json对象或者数组	 * 	 * @param urlString	 * @param res	 */	public static void get(String urlString, JsonHttpResponseHandler res) {		client.get(urlString, res);	}	/**	 * get,带参数,获取json对象或者数组	 * 	 * @param urlString	 * @param params	 * @param res	 */	public static void get(String urlString, RequestParams params,                           JsonHttpResponseHandler 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,不带参数,获取json对象或者数组	 * 	 * @param urlString	 * @param res	 */	public static void post(String urlString, JsonHttpResponseHandler res) {		client.post(urlString, res);	}	/**	 * post,带参数,获取json对象或者数组	 * 	 * @param urlString	 * @param params	 * @param res	 */	public static void post(String urlString, RequestParams params,                            JsonHttpResponseHandler 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("captcha", captcha);		params.add("encodedService", encodedService);		params.add("loginErrCnt", loginErrCnt);		params.add("lt", lt);		params.add("password", password);		params.add("username", username);		params.add("service", service);		params.add("serviceName", serviceName);		return params;	}	/**	 * 获取请求首页参数	 * @return	 */	public static RequestParams gethomeRequestParams(){		RequestParams params = new RequestParams();		params.add("ticket",ticket);		return params;	}}
定义完HTTP客户端,我们就需要去模拟登录,当然模拟登录必须要设置CookieStore如下所示:

/**     * 初始化Cookie     */    private void initCookie(Context context) {        //必须在请求前初始化        cookie = new PersistentCookieStore(context);        HttpUtil.getClient().setCookieStore(cookie);        httpClient.setCookieStore(cookie);    }
然后在请求登录页面时候就可以将返回的cookie设置给HTTPClient了。

难到这样就可以了?那那个lt值怎么获取传递过去昵?先贴一段获取lt的代码后面做解释:

/**     * 获取lt     */    private void getlt(){        Document doc = null;            doc = Jsoup.parse(GetShouye());            if (doc != null)        if (doc != null) {            Elements login = doc.body().getElementsByClass("login_form");            Document containerDoc = Jsoup.parse(login.toString());            Elements ddd = containerDoc.getElementsByTag("input");            for (Element aaa : ddd) {                if (aaa.toString().contains("lt")) {                    String l = aaa.toString();                    String lt = l.substring(38, l.length() - 4);                    HttpUtil.lt = lt;                }            }        }    } /**     * 请求首页     * @return     */    public String GetShouye()    {        String result= "";//    	创建HttpGet或HttpPost对象,将要请求的URL通过构造方法传入HttpGet或HttpPost对象。        if (urlll == null){            urlll = HttpUtil.URL_MAIN;        }        HttpGet httpRequst = new HttpGet(urlll);//    	new DefaultHttpClient().execute(HttpUriRequst requst);        try {            //使用DefaultHttpClient类的execute方法发送HTTP GET请求,并返回HttpResponse对象。            HttpResponse httpResponse = httpClient.execute(httpRequst);//其中HttpGet是HttpUriRequst的子类            if (httpResponse.getStatusLine().getStatusCode() == 200) {                HttpEntity httpEntity = httpResponse.getEntity();                result = EntityUtils.toString(httpEntity);//取出应答字符串                // 一般来说都要删除多余的字符                result.replaceAll("\r", "");//去掉返回结果中的"\r"字符,否则会在结果字符串后面显示一个小方格            } else{                httpRequst.abort();            }        }catch (ClientProtocolException e) {            // TODO Auto-generated catch block            e.printStackTrace();            result = e.getMessage().toString();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();            result = e.getMessage().toString();        }        return result;    }
所有准备工作做好我们就可以发送登录请求了

/**     * 登录     */    private void login() {        HttpUtil.username = username.getText().toString().trim();        String md5 = "@";        try {            md5= MD5Until.GetMD5Code(password.getText().toString().trim());        } catch (Exception e) {            e.printStackTrace();        }        Log.d("md5  ",md5);        HttpUtil.password = md5;        //需要时打开验证码注释        HttpUtil.captcha = secrectCode.getText().toString().trim();        Log.d("cookie",HttpUtil.Cookie);        if (TextUtils.isEmpty(HttpUtil.username)                || TextUtils.isEmpty(HttpUtil.password)) {            Toast.makeText(getApplicationContext(), "账号或者密码不能为空!",                    Toast.LENGTH_SHORT).show();            return;        }        final ProgressDialog dialog =CommonUtil.getProcessDialog(BindActivity.this,"正在登录中!!!");        dialog.show();        RequestParams params = HttpUtil.getLoginRequestParams();// 获得请求参数        Log.d("http",params.toString());        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");                            Log.d("Header",arg1.toString());                            Log.d("resunt",resultContent);//							List
list = Getlt.match(resultContent,"input","name=\"lt\" ");// Log.d("list",list.toString()); if(linkService.isLogin(resultContent)!=null){ String ret = linkService.parseMenu(resultContent); Log.d("cas", "login success:"+ret); GetHerfUrl(resultContent); Toast.makeText(getApplicationContext(), "登录成功!!!", Toast.LENGTH_SHORT).show(); new Thread(new Runnable() { @Override public void run() { String s = GetShouye(); Log.d("sdfsdaf",s); String ss = Getksap(); Log.d("qqqqqq",ss); jump2Main(); } }).start(); }else{ getCode(); 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(); } }); }
登录成功后会返回一个链接,并不是登录后的教务首页,我们还需要解析提取链接并用GET请求去请求,即可!下面我们就来说一下关于HTML网页数据的解析。

JSOUP对HTML网页数据的解析

       JSOUP是一个非常好用的,用JAVA来抓取网页数据的开源框架。使用起来也非常简单,就是根据HTML内容,先获取BODY====》》》提取字段对应的class====》》》对应的标签ID。我们还是以lt为例来看:

/**     * 获取lt     */    private void getlt(){        Document doc = null;            doc = Jsoup.parse(GetShouye());            if (doc != null)        if (doc != null) {            //获取解析后的body及对应的class——login_form            Elements login = doc.body().getElementsByClass("login_form");            Document containerDoc = Jsoup.parse(login.toString());            //获取到标签id对应的数据            Elements ddd = containerDoc.getElementsByTag("input");            for (Element aaa : ddd) {                //从这些数据中提取出lt                if (aaa.toString().contains("lt")) {                    String l = aaa.toString();                    String lt = l.substring(38, l.length() - 4);                    HttpUtil.lt = lt;                }            }        }    }
后面所有的数据获取都是这样,不过后面都是解析table而已。下面觉得重点应该说一下对课程信息的存储。

课程表信息的存储与读取

      课程表信息的存储也是非常麻烦的一件事,当时没想明白,存储完之后发现读到的数据全乱了。

{\"Course_address\":\"第12教研楼405\",\"Course_name\":\"网站规划与设计\",        \"Course_teacher\":\"李君君\",\"Course_type\":\"必修\",\"Course_week\":\"周一第3,4节{第1-16周}\"},
上面是用JSON数组来存储每节课信息的存储结构,绘制课程表时,根据课表中的字段。如下所示:

public class CourseActivity extends Activity {    private ArrayList
mCourse; private SharedPreferences mShaerPreferences; // 每天有多少节课 private int mMaxCouese; // 一共有多少周 private int mMaxWeek; // 现在是第几周 private int mNowWeek; // 左边一节课的高度 private float mLeftHeight; // 左边一节课的宽度 private float mLeftWidth; private TextView mChangeWeek; private LinearLayout mLeftNo; private LinearLayout mMonday; private LinearLayout mTuesday; private LinearLayout mWednesday; private LinearLayout mThursday; private LinearLayout mFirday; private LinearLayout mSaturday; private LinearLayout mWeekend; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_course); // 实例化所有对象 initCtrl(); // 初始化数据 initData(); // 绘制左边的课程节数 drawLeftNo(); // 绘制当前周 drawNowWeek(); // 绘制所有课程 其实可以使用redrawAll替代三步 drawAllCourse(); } /** * 实例化所有对象 */ private void initCtrl() { mChangeWeek = (TextView) findViewById(R.id.changeWeek); mLeftNo = (LinearLayout) findViewById(R.id.leftNo); mMonday = (LinearLayout) findViewById(R.id.monday); mTuesday = (LinearLayout) findViewById(R.id.tuesday); mWednesday = (LinearLayout) findViewById(R.id.wednesday); mThursday = (LinearLayout) findViewById(R.id.thursday); mFirday = (LinearLayout) findViewById(R.id.firday); mSaturday = (LinearLayout) findViewById(R.id.saturday); mWeekend = (LinearLayout) findViewById(R.id.weekend); } /** * 初始化所有数据 */ private void initData() { // 初始化课表 praseJson(); // 读取配置信息 readIniFile(); // 点击选择切换周 mChangeWeek.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showChangeWeekDlg(v); } }); } /** * 绘制左边的课程节数 */ private void drawLeftNo() { mLeftHeight = getResources().getDimension(R.dimen.left_height); mLeftWidth = getResources().getDimension(R.dimen.left_width); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( (int) mLeftWidth, (int) mLeftHeight); for (int i = 1; i <= mMaxCouese; i++) { TextView tv = new TextView(this); tv.setText(i + ""); tv.setGravity(Gravity.CENTER); tv.setTextColor(getResources().getColor(R.color.font)); tv.setBackgroundResource(R.drawable.boder); mLeftNo.addView(tv, lp); } } /** * 绘制课表 * * @param ll * 绘制课表到哪一个LinearLayout上 * @param dayOfWeek * 绘制的数据来自周几 一二三四五六七 */ private void drawCourse(LinearLayout ll, char dayOfWeek) { // 删除所有子View ll.removeAllViews(); // 上一节课结束是第几节 int perCourse = -1; for (CourseBean course : mCourse) { // 判断是否显示这节课 // 是不是同一天 是不是这一周 if (course.getDayOfWeek() != dayOfWeek || !course.inThisWeek(mNowWeek)) continue; // 设置TextView的属性样式 TextView tv = new TextView(this); tv.setText(course.getCourse_name() + "\n@" + course.getCourse_address()); tv.setBackgroundResource(R.drawable.course); tv.setTextColor(getResources().getColor(R.color.course_font_color)); tv.setTextSize(12); // 将数据绑定到TextView上 tv.setTag(course); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { CourseBean tag = (CourseBean) v.getTag(); showCouseDetails(tag); } }); // 设置TextView的位置 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, (int) (course.getStep() * mLeftHeight)); // 说明这节课为第一节课 if (perCourse == -1) { lp.setMargins(1, (int) ((course.getMinCourse() - 1) * mLeftHeight), 1, 0); // useHeight = (int) ((course.getMaxCourse()-1) * mLeftHeight); } else { lp.setMargins(1, (course.getMinCourse() - perCourse - 1) * (int) mLeftHeight, 1, 0); // useHeight = useHeight + (course.getMaxCourse() - perCourse - // 1)* (int) mLeftHeight; } perCourse = course.getMaxCourse(); ll.addView(tv, lp); } } /** * 绘制当前周 */ private void drawNowWeek() { mChangeWeek.setText("第" + mNowWeek + "周"); } /** * 重新绘制所有,不包括标题栏和星期几 在修改每天的节数后调用 */ /*private void redrawAll() { drawLeftNo(); drawNowWeek(); drawAllCourse(); }*/ /** * 绘制课程,用于周数切换以后 */ private void drawAllCourse() { drawCourse(mMonday, '一'); drawCourse(mTuesday, '二'); drawCourse(mWednesday, '三'); drawCourse(mThursday, '四'); drawCourse(mFirday, '五'); drawCourse(mSaturday, '六'); drawCourse(mWeekend, '日'); } /** * 读取配置信息 */ private void readIniFile() { mShaerPreferences = getSharedPreferences("iniFile", Context.MODE_PRIVATE); mMaxCouese = mShaerPreferences.getInt("mMaxCouese", -1); mMaxWeek = mShaerPreferences.getInt("mMaxWeek", -1); mNowWeek = mShaerPreferences.getInt("mNowWeek", -1); Editor edit = mShaerPreferences.edit(); // 默认12节课 if (mMaxCouese == -1) { edit.putInt("mMaxCouese", 12); } // 默认20周 if (mMaxWeek == -1) { edit.putInt("mMaxWeek", 17); } // 默认第一周 if (mNowWeek == -1) { edit.putInt("mNowWeek", 15); } edit.commit(); } /** * 解析来自strings.xml里面的Json课表数据 */ private void praseJson() { String json = getResources().getString(R.string.kb); Gson gson = new Gson(); mCourse = gson.fromJson(json, new TypeToken
>() { }.getType()); } /** * 弹出窗口,显示课程详细信息 * */ public void showCouseDetails(CourseBean bean) { AlertDialog.Builder builder = new Builder(this); AlertDialog dialog = builder.create(); dialog.show(); dialog.setContentView(R.layout.details_layout); TextView textView = (TextView) dialog.findViewById(R.id.name); textView.setText(bean.getCourse_name()); textView = (TextView) dialog.findViewById(R.id.type); textView.setVisibility(View.GONE); textView.setText(bean.getCourse_type()); textView = (TextView) dialog.findViewById(R.id.teacher); textView.setText(bean.getCourse_teacher()); textView = (TextView) dialog.findViewById(R.id.address); textView.setText(bean.getCourse_address()); textView = (TextView) dialog.findViewById(R.id.week); textView.setText(bean.getCourse_week()); } /** * 显示切换当前周的窗口 */ public void showChangeWeekDlg(View v) { View view = View.inflate(this, R.layout.changweek_layout, null); ListView weekList = (ListView) view.findViewById(R.id.weekList); ArrayList
strList = new ArrayList
(); for (int i = 1; i < mMaxWeek; i++) { strList.add("第" + i + "周"); } ArrayAdapter
adapter = new ArrayAdapter
(this, R.layout.item, strList); weekList.setAdapter(adapter); view.measure(0, 0); final PopupWindow pop = new PopupWindow(view, 300, 500, true); pop.setBackgroundDrawable(new ColorDrawable(0x00000000)); int xOffSet = -(pop.getWidth() - v.getWidth()) / 2; pop.showAsDropDown(v, xOffSet, 0); weekList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView
adapter, View view, int positon, long id) { mNowWeek = positon + 1; pop.dismiss(); drawNowWeek(); drawAllCourse(); } }); }}

你可能感兴趣的文章
openstack安装(liberty)--安装认证服务(Identity service)
查看>>
邮件服务器软件为企业分支“搭桥”
查看>>
Windows Azure之VM的迁移之旅
查看>>
DevOps系列——Gogs和Jenkins的CI配置
查看>>
ExtJS4.2学习(php版)
查看>>
负载均衡——HAProxy
查看>>
win7 访问本机的虚拟机中centos的web项目
查看>>
批处理之播放文本文件
查看>>
windows server 2008活动目录的备份与还原
查看>>
spring boot 2.0.1.RELEASE hibernate 缓存 ehcache 详解
查看>>
关于windows7的更新update失败,windows media play安装失败的 ...
查看>>
mysql之慢查询
查看>>
我的友情链接
查看>>
linux mysql 主主同步复制
查看>>
使用 ajax json(getJSON)从服务器读取数据,在IE下不更新问题解决
查看>>
Pythonの豆瓣相册下载脚本
查看>>
linux shell4 判断登陆用户,然后重启关闭系统
查看>>
《Cisco路由器配置与管理完全手册》(第二版)前言和目录
查看>>
git 删除本地分支
查看>>
关于SQL SERVER高并发解决方案
查看>>