Android实现一键获取课程成绩dome
欢迎转载但请标明出处:http://blog.csdn.net/android_for_james/article/details/50984493
两周废寝忘食的创作最终成功了,如今拿出来分享一下。
先不说别的看一下程序执行效果图。我没怎么设计ui所以界面不是非常好看可是能说明问题~~~
如今我们来看看实现这个功能须要些什么准备工作,我们须要网络信息抓取工具一般windows能够用httpwatch我是mac系统所以我具体介绍一下mac上面的工具
1.Charles
2.Google Chrome
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="100" height="100" alt="">
接下来就能够进行网页信息抓包了,先看一下我们学校的教务网页
对浏览器抓包我使用的是Charles首先要配置成下图这个样子然后才干够抓包
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="500" height="300" alt="">
首先对登录信息抓包时要找以下这张截图上的信息
然后对获取验证吗抓包
我们在response中能够看到返回信息。
然后我们须要下载一个解析HTML源代码的架构包叫Jsoup放到libs文件夹下然后右键选择
as library
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="300" height="300" alt="">
这些就准备好了然后我们来看一小段HTML代码方便解析时候解说
<br> <table width="100%" class="datalist"> <tr> <th> 学年 </th> <th> 学期 </th> <th> 课程号 </th> <th> 课序号 </th> <th> 课程名 </th> <th> 选课属性 </th> <th> 课组 </th> <th> 学分 </th> <th> 平时 </th> <th> 期末 </th> <th> 总评 </th> <th> 是否缓考 </th> <th> 考试性质 </th> <th> 备注 </th> <th> 主讲教师 </th> <th> 课程类别 </th> </tr>
能够看到我们须要的信息都被一个一个的标签包裹着。Jsoup所做的就是将我们须要的信息从标签里剥离出来。
然后我们来看代码实现。我这里分了两个Activity来实现
1.MainActivity
public class MainActivity extends ActionBarActivity {
//使用SharedPreferences进行用户的usernamepassword以及cookie的保存
SharedPreferences sharedPreferences;
SharedPreferences.Editor editor; private EditText studentNumber;
private EditText passWord;
private EditText idCode; private Bitmap bitmap;
private ImageView IdcodeImage; //注意这里Handler使用的是import android.os.Handler;这个包
private Handler handler; private Button logIn; String StudentNumber;
String PassWord;
String IdCode; String groupId="";
String login="登录";
//这条是解析出来进行获取验证码的图片的网址
String url2="http://jw.djtu.edu.cn/academic/getCaptcha.do";
//这条是解析出来进行提交登录信息的网址
String url3="http://jw.djtu.edu.cn/academic/j_acegi_security_check";
//这里使用HttpClient进行数据的获取和提交
HttpClient client; @Override
protected void onCreate(final Bundle savedInstanceState)
{ super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initEvent();
//获取验证码
getIdCode();
//对我们的验证码绑定一个单击响应事件,这是为了去实现验证码看不清时再更新一张验证码而用
IdcodeImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getIdCode();
}
});
//对登录button绑定单击响应事件
logIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
saveEvent();
loginEvent();
}
});
}
public void initEvent()
{
//实例化HttpClient对象
client=new DefaultHttpClient();
//sharedPreferences第一个參数是给你保存的信息起个名字,第二个參数设置为Context.MODE_PRIVATE属性。
// 这样会避免其它应用能够直接訪问我们保存的信息
sharedPreferences=getSharedPreferences("params", Context.MODE_PRIVATE);
//实例化SharedPreferences.Editor对象
editor=sharedPreferences.edit();
studentNumber=(EditText)findViewById(R.id.studentNumber);
passWord=(EditText)findViewById(R.id.key);
IdcodeImage=(ImageView)findViewById(R.id.passImage);
idCode=(EditText)findViewById(R.id.identifyingCode);
logIn=(Button)findViewById(R.id.login);
//实例化Handler对象方便线程之间通信
handler =new Handler();
}
public void getIdCode()
{
new Thread() {
@Override
public void run() {
//我们须要同步Cookie信息所以从验证码開始就须要获取Cookie
List<Cookie> cookies1;
//HttpGet来发送获取验证码请求
HttpGet httpGet = new HttpGet(url2);
//声明一个HttpResponse
HttpResponse httpResponse = null;
try {
//实例化HttpResponse
httpResponse = client.execute(httpGet);
} catch (IOException e) {
e.printStackTrace();
}
//假设server响应成功
if (httpResponse.getStatusLine().getStatusCode() == 200) {
try {
//使用输入流来接受数据
InputStream in = httpResponse.getEntity().getContent();
//bitmap来获取数据流中的图片信息
bitmap = BitmapFactory.decodeStream(in);
//关闭输入流
in.close();
String Cookies;
//获取Cookie
cookies1 = ((AbstractHttpClient) client).getCookieStore().getCookies();
Cookies = "JSESSIONID="+cookies1.get(0).getValue().toString();
//System.out.println(Cookies);
//在SharedPreferences中保存cookie
editor.putString("Cookies", Cookies);
//提交保存数据
editor.commit();
//通过handler.post方法在线程中更新主线程中的验证码图片信息
handler.post(new Runnable() {
@Override
public void run() {
if (bitmap != null) {
IdcodeImage.setImageBitmap(bitmap);
}
}
}); } catch (IOException e) {
e.printStackTrace();
}
} }
}.start();
}
public void saveEvent()
{
//获取输入信息。并保存为做记住password来铺垫
StudentNumber =studentNumber.getText().toString();
PassWord =passWord.getText().toString();
IdCode = idCode.getText().toString();
//这里写入StudentNumber和PassWord是为了做记住password登录
editor.putString("StudentNumber", StudentNumber);
editor.putString("PassWord", PassWord);
editor.putString("IdCode", IdCode);
editor.commit();
}
public void loginEvent()
{ new Thread() {
@Override
public void run() {
//提交数据用List<NameValuePair>的方式
List<NameValuePair> params = new ArrayList<NameValuePair>();
//这里的名称不要有多余的符号,由于提交数据时httppost方法会帮你维护数据
//这里表单的数据顺序要依照刚刚解析所显示的顺序排列
params.add(new BasicNameValuePair("groupId", groupId));
params.add(new BasicNameValuePair("j_username", StudentNumber));
params.add(new BasicNameValuePair("login",login));
params.add(new BasicNameValuePair("j_password", PassWord));
params.add(new BasicNameValuePair("j_captcha", IdCode));
System.out.println(params);
try {
HttpPost httpPost = new HttpPost(url3);
String Cookies;
//获取到刚刚在获取验证码时得到的Cookie
Cookies = sharedPreferences.getString("Cookies", null);
//System.out.println(Cookies);
//提交数据做准备
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
//同步cookie
httpPost.setHeader("Cookie", Cookies);
//获取返回的信息
HttpResponse httpResponse = client.execute(httpPost);
String result = EntityUtils.toString(httpResponse.getEntity());
//System.out.println(result);
//这里我们不仅须要保证server正常响应,并且还要知道当我们登陆失败时是什么原因导致的
if(!result.contains("错误提示")&&httpResponse.getStatusLine().getStatusCode() == 200)
{
startActivity(new Intent(MainActivity.this, Score_find.class));
}
else
{
if(result.contains("password不匹配"))
{ handler.post(new Runnable() {
@Override
public void run() { Toast.makeText(MainActivity.this, "password不匹配或username错误!!!请又一次输入", Toast.LENGTH_LONG).show();
//当登陆失败时上一张验证码的图片已经失效因此需又一次载入
getIdCode(); }
});
}else if(result.contains("验证码错误")||result.contains("验证码不对"))
{
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "验证码错误!!!请又一次输入", Toast.LENGTH_LONG).show();
getIdCode();
}
});
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start(); }
}
2.Score_find Activity
public class Score_find extends ActionBarActivity{
private String Cookies;
HttpClient client;
private String url="http://jw.djtu.edu.cn/academic/manager/score/studentOwnScore.do?groupId=&moduleId=2021";
private String year=null;
private String trem=null;
private String para="0";
private String sortColumn="";
private String Submit="查询"; private TextView showScore; private EditText InputYear;
private EditText InputTrem; SharedPreferences sharedPreferences;
StringBuffer sb=new StringBuffer(); private Handler handler=null; private Button searchButton;
//这两个标记是用于推断用户输入的数据是否合法
private int mark1=0;
private int mark2=0;
@Override
protected void onCreate(final Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
setContentView(R.layout.score_find);
initEvent();
searchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String years=null,trems=null;
years=InputYear.getText().toString();
trems=InputTrem.getText().toString();
System.out.println(years+trems);
//输入信息的推断
if("春".equals(trems))
{
trem="1";
mark1=1;
System.out.println(trems+"\t"+trem);
}else if("秋".equals(trems))
{
trem="2";
mark1=1;
}else
{
mark1=0;
Toast.makeText(Score_find.this,"输入学期有误请又一次输入!",Toast.LENGTH_SHORT).show();
}
if("2014".equals(years))
{
year="34";
mark2=1;
System.out.println(years+"\t"+year);
}else if("2015".equals(years))
{
year="35";
mark2=1;
}
else
{
mark2=0;
Toast.makeText(Score_find.this,"输入年份有误请又一次输入! ",Toast.LENGTH_SHORT).show();
}
//假设两个信息都输入合法则提交请求
if(mark1==1&&mark2==1) {
//是耗时操作都要放到新线程里运行
getScore();
}
}
}); }
public void initEvent()
{
InputTrem=(EditText)findViewById(R.id.InputTrem);
InputYear=(EditText)findViewById(R.id.InputYear);
searchButton=(Button)findViewById(R.id.searchButton);
showScore=(TextView)findViewById(R.id.show_score);
//设置showScore能够滚动
showScore.setMovementMethod(ScrollingMovementMethod.getInstance());
handler=new Handler()
{
@Override
public void handleMessage(Message message)
{
//载入信息
showScore.setText(sb.toString());
}
};
sharedPreferences = getSharedPreferences("params", Context.MODE_PRIVATE);
Cookies=sharedPreferences.getString("Cookies", null);
showScore=(TextView)findViewById(R.id.show_score);
client=new DefaultHttpClient();
}
public void analysisText(String results)
{
//这里使用jsoup开源的解析包进行html源代码的解析
//获取要解析的网址或者文档或者网址
Document document = Jsoup.parse(results);
//经过分析成绩保存在datalist这个Class中因此要定位到这个类中
Elements elements = document.getElementsByClass("datalist");
//获取他的第一个元素集合
Element element = elements.get(0);
//再分析能够看到在tr标签下有成绩的具体信息
Elements elements1 = element.getElementsByTag("tr");
Element element2;
Elements elements3;
Element element3;
Element element4;
for (int i = 0; i < elements1.size(); i++) {
//剥离每个标签
element2 = elements1.get(i);
//再又一次定位td标签下的内容
elements3 = element2.getElementsByTag("td");
for (int j = 0; j < elements3.size(); j++) {
//这里为了获取td标签中的子元素要进行一个循环
if (j == 0) {
//我发现我要的课程名和成绩分别在elements3集合中的第5个元素和第11个元素
element3 = elements3.get(4);
element4 = elements3.get(10);
sb.append(element3.text()).append(":").append("\t\t").append(element4.text()).append("\n");
} else {
break;
} }
}
//数据获取完毕通知组件重绘信息
handler.sendEmptyMessage(0); }
public void getScore()
{
new Thread() {
@Override
public void run()
{
HttpResponse httpResponse;
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("year", year));
params.add(new BasicNameValuePair("term", trem));
params.add(new BasicNameValuePair("para", para));
params.add(new BasicNameValuePair("sortColumn", sortColumn));
params.add(new BasicNameValuePair("Submit", Submit));
System.out.println(params);
httpPost.setHeader("Cookie", Cookies);
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = client.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == 200) { StringBuffer stringBuffer = new StringBuffer();
String result;
InputStream inputStream = httpResponse.getEntity().getContent();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String data = "";
//读取得到的数据
while ((data = bufferedReader.readLine()) != null) {
stringBuffer.append(data);
stringBuffer.append("\n");
}
result = stringBuffer.toString();
//推断是否获取到数据
if (result == null) {
System.out.println("NULL!!!!");
} else {
analysisText(result);
} }
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} }
}.start();
} }
最后我用红字强调一下做这个demo我一開始失败了好多次的地方:
1.Cookie信息获取要在获取验证码时同一时候获取。
2.假设线程操作封装成类的话会造成SharedPreferences绑定content失败。
3.对HTML源代码进行解析的时候我尝试将解析方法写成一个函数,可是会出现一些未捕获的错误而导致程序崩溃。因此我把解析步骤也放到了新线程中。
希望对大家有所帮助,欢迎转载但要标明出处,谢谢!
有什么不足的地方能够留言给我我会尽快回复并改正!
欢迎关注我的博客:http://blog.csdn.net/android_for_james
源代码下载网址(点开后面链接后在文章末尾有点击下载button):点击打开链接
Android实现一键获取课程成绩dome的更多相关文章
- 学生各门课程成绩统计SQL语句大全
学生成绩表(stuscore): 姓名:name 课程:subject 分数:score 学号:stuid 张三 数学 89 1 张三 语文 80 1 张三 英语 70 1 李四 数学 90 2 李四 ...
- xamarin.android之 Android 4.4+ 获取图片真实路径
Android 4.4以下 选择图片是可以获取到图片路径的.高于Android 4.4获取图片路径只是获取到一个图片编号. 所以需要针对Android版本进行路径解析: #region 高于 v4.4 ...
- android 通过socket获取IP
如题<android 通过socket获取IP>: socket.getInetAddress().getHostAddress();
- 【风马一族_Android】Android 从命令行界面获取手机信息
Android 从命令行界面获取手机信息 1: cmd 打开命令行界面 2:adb devices 获取与电脑相连的设备,例如:模拟器.真机(手机) (右击“标记”,选择设备名称,点击“Ctrl+ ...
- sql查询每门课程成绩最高的学生
给出数据库(sco)如下图: 查出每门课程成绩最高的学生 select b.id,b.kemu,b.name,b.chengji from (select kemu,max(chengji) maxc ...
- Android开发之获取xml文件的输入流对象
介绍两种Android开发中获取xml文件的输入流对象 第一种:通过assets目录获取 1.首先是在Project下app/src/main目录下创建一个assets文件夹,将需要获取的xml文件放 ...
- [转帖]一键获取 所有连接过的wifi 密码
cmd 一键获取 所有连接过的wifi 密码 转帖来源: http://www.cnblogs.com/hookjoy/p/5537623.html for /f "skip=9 token ...
- 双心一键获取winsxs的写入权限,解决VC运行库安装error1935错误
@Echo offtitle 双心一键获取winsxs的写入权限,解决VC运行库安装error1935等错误set path=%path%;%~dp0setlocal EnableDelayedExp ...
- [源码]一键获取windows系统登陆密码vc6版源码
[源码]一键获取windows系统登陆密码vc6版源码支持:XP/2000/2003/WIN7/2008等 此版本编译出来的程序体积较小几十KB... 而vs版则1点几M,体积整整大了2-30倍对某些 ...
随机推荐
- 陕西师范大学第七届程序设计竞赛网络同步赛 I 排队排队排队【数组任一位可以移动到队头,最少移动几次增序/数组指针操作】
链接:https://www.nowcoder.com/acm/contest/121/I来源:牛客网 题目描述 ACM竞赛队内要开运动会啦!!!! 竞赛队内的一群阳光乐观积极的队员们迅速的在操场上站 ...
- Python的支持工具[0] -> 环境包管理工具[1] -> Anaconda
Anaconda包管理工具 / Anaconda Package Management Tools Anaconda is the world’s most popular Python data s ...
- SSO [ OAuth2.0 ]
1) SSO英文全称Single Sign On,单点登录. SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 它包括可以将这次主要的登录映射到其他应用中用于同一个用户的 ...
- Android之Activity的启动模式
启动模式有4种,分别为:1.standard(默认) -- 标准2.singleTop -- 单顶3.singleTask -- 单任务4.singleInstance: -- 单例 -- 如果 ...
- Jackson的高级应用(转)
Jackson 是当前用的比较广泛的,用来序列化和反序列化 JSON 的 Java 的开源框架.Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson ...
- 无法通过windows installer服务安装此安装程序包。您必须安装带有更新版本windows Installer服务的Windows
无法通过windows installer服务安装此安装程序包.您必须安装带有更新版本windows installer服务的Windows 出现这个问题不让安装程序,可以到微软网站更新Windows ...
- Cannot assign requested address 问题
測试一个简单的TCP套接字程序,client向server请求建立连接然后释放. 在一台主机上同一时候执行两个client时.出现报错"Cannot assign requestedaddr ...
- 图文介绍openLDAP在windows上的安装配置
目录 概述 测试环境 安装过程 配置启动 客户端介绍 多级DC的ldif文件的配置 [一].概述 什么叫LDAP呢,概念的东西这里就不多讲了,网上搜索下有很多,本文的重点是介绍如何在windows平台 ...
- MySQL高可用解决方案MMM
一.MMM简介: MMM即Multi-Master Replication Manager for MySQL:mysql多主复制管理器,基于perl实现,关于mysql主主复制配置的监控.故障转移和 ...
- #include <>与#include""区别
<>先去系统目录中找头文件,如果没有在到当前目录下找.所以像标准的头文件 stdio.h.stdlib.h等用这个方法. 而""首先在当前目录下寻找,如果找不到,再到系 ...