欢迎转载但请标明出处: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的更多相关文章

  1. 学生各门课程成绩统计SQL语句大全

    学生成绩表(stuscore): 姓名:name 课程:subject 分数:score 学号:stuid 张三 数学 89 1 张三 语文 80 1 张三 英语 70 1 李四 数学 90 2 李四 ...

  2. xamarin.android之 Android 4.4+ 获取图片真实路径

    Android 4.4以下 选择图片是可以获取到图片路径的.高于Android 4.4获取图片路径只是获取到一个图片编号. 所以需要针对Android版本进行路径解析: #region 高于 v4.4 ...

  3. android 通过socket获取IP

    如题<android 通过socket获取IP>: socket.getInetAddress().getHostAddress();

  4. 【风马一族_Android】Android 从命令行界面获取手机信息

    Android 从命令行界面获取手机信息 1: cmd 打开命令行界面 2:adb devices   获取与电脑相连的设备,例如:模拟器.真机(手机) (右击“标记”,选择设备名称,点击“Ctrl+ ...

  5. sql查询每门课程成绩最高的学生

    给出数据库(sco)如下图: 查出每门课程成绩最高的学生 select b.id,b.kemu,b.name,b.chengji from (select kemu,max(chengji) maxc ...

  6. Android开发之获取xml文件的输入流对象

    介绍两种Android开发中获取xml文件的输入流对象 第一种:通过assets目录获取 1.首先是在Project下app/src/main目录下创建一个assets文件夹,将需要获取的xml文件放 ...

  7. [转帖]一键获取 所有连接过的wifi 密码

    cmd 一键获取 所有连接过的wifi 密码 转帖来源: http://www.cnblogs.com/hookjoy/p/5537623.html for /f "skip=9 token ...

  8. 双心一键获取winsxs的写入权限,解决VC运行库安装error1935错误

    @Echo offtitle 双心一键获取winsxs的写入权限,解决VC运行库安装error1935等错误set path=%path%;%~dp0setlocal EnableDelayedExp ...

  9. [源码]一键获取windows系统登陆密码vc6版源码

    [源码]一键获取windows系统登陆密码vc6版源码支持:XP/2000/2003/WIN7/2008等 此版本编译出来的程序体积较小几十KB... 而vs版则1点几M,体积整整大了2-30倍对某些 ...

随机推荐

  1. 陕西师范大学第七届程序设计竞赛网络同步赛 I 排队排队排队【数组任一位可以移动到队头,最少移动几次增序/数组指针操作】

    链接:https://www.nowcoder.com/acm/contest/121/I来源:牛客网 题目描述 ACM竞赛队内要开运动会啦!!!! 竞赛队内的一群阳光乐观积极的队员们迅速的在操场上站 ...

  2. Python的支持工具[0] -> 环境包管理工具[1] -> Anaconda

    Anaconda包管理工具 / Anaconda Package Management Tools Anaconda is the world’s most popular Python data s ...

  3. SSO [ OAuth2.0 ]

    1) SSO英文全称Single Sign On,单点登录. SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 它包括可以将这次主要的登录映射到其他应用中用于同一个用户的 ...

  4. Android之Activity的启动模式

    启动模式有4种,分别为:1.standard(默认)  -- 标准2.singleTop  -- 单顶3.singleTask -- 单任务4.singleInstance: -- 单例  -- 如果 ...

  5. Jackson的高级应用(转)

    Jackson 是当前用的比较广泛的,用来序列化和反序列化 JSON 的 Java 的开源框架.Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson ...

  6. 无法通过windows installer服务安装此安装程序包。您必须安装带有更新版本windows Installer服务的Windows

    无法通过windows installer服务安装此安装程序包.您必须安装带有更新版本windows installer服务的Windows 出现这个问题不让安装程序,可以到微软网站更新Windows ...

  7. Cannot assign requested address 问题

    測试一个简单的TCP套接字程序,client向server请求建立连接然后释放. 在一台主机上同一时候执行两个client时.出现报错"Cannot assign requestedaddr ...

  8. 图文介绍openLDAP在windows上的安装配置

    目录 概述 测试环境 安装过程 配置启动 客户端介绍 多级DC的ldif文件的配置 [一].概述 什么叫LDAP呢,概念的东西这里就不多讲了,网上搜索下有很多,本文的重点是介绍如何在windows平台 ...

  9. MySQL高可用解决方案MMM

    一.MMM简介: MMM即Multi-Master Replication Manager for MySQL:mysql多主复制管理器,基于perl实现,关于mysql主主复制配置的监控.故障转移和 ...

  10. #include <>与#include""区别

    <>先去系统目录中找头文件,如果没有在到当前目录下找.所以像标准的头文件 stdio.h.stdlib.h等用这个方法. 而""首先在当前目录下寻找,如果找不到,再到系 ...