杨培文

instabtbu-教务管理

让我们来了解一下instabtbu的教务管理功能开发历史

首先我们为了用户体验,决定采用识别验证码技术来解决验证码难认,验证码小等问题.

这是最初我们识别的时候,matlab输出的数据,此时我们的验证码准确率已经达到100%(样本超过50张),之后我们把识别验证码的代码从matlab语言翻译到C语言.(matlab源码也是原创)

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

public String shibie(Bitmap myBitmap)
{
String yzm = "";
String[] myyzm = {"1","2","3","b","c","m","n","v","x","z"};
try{
int qietu,duibi;
int x,y;
for(qietu=0;qietu<4;qietu++)
{
int errnum[]={100,100,100,100,100,100,100,100,100,100};
for(duibi=0;duibi<10;duibi++)
{
int errpixel=0;
InputStream is = getResources().getAssets().open(myyzm[duibi]+".bmp");
Bitmap rawBitmap = BitmapFactory.decodeStream(is);
for(y=0;y<12;y++)
{
for(x=0;x<9;x++)
{
int col = myBitmap.getPixel(x+3+10*qietu, y+4);
int col2 = rawBitmap.getPixel(x,y);
if((col2&0xFF)>(col&0xFF))errpixel++;
}
System.out.print("nn");
}
errnum[duibi]=errpixel;
}
int wz=0,min=100,i;
for(i=0;i<10;i++)if(errnum[i]<min){min=errnum[i];wz=i;}
yzm+=myyzm[wz];
}
}catch(Exception e)
{}
System.out.println(yzm);
return yzm;
}

识别验证码其实并不难,上面的源码已经给出来了,实际上就是一张张图去比较,谁的错误点最少那么就是谁拉.由于识别并不难这里我不详细讲了.验证码组成元素只有十个,分别是123bcmnvxz.

这个就是在安卓上面识别的效果.识别好验证码之后,我们就开始做登录.本来是想做得跟上网一样,后来一想,反正同学们每次进教务仅仅只是查成绩或者查课表,干脆就一键查吧,于是登录按钮被砍掉了.

好了,让我们看登录怎么做吧! 嘛,假设你已经会了POST,如果不会请看上网登录是怎么写出来的. 会了POST之后呢,我们就可以通过Chrome的F12开发者工具截包,找到登录的数据包的结构,是这样的:

http://jwgl.btbu.edu.cn/Logon.do method=logon&USERNAME=学号&PASSWORD=密码&RANDOMCODE=验证码

OK,我们按照这个格式发送过去,得到的返回数据如果包含“http://jwgl.btbu.edu.cn/framework/main.jsp”这一串数据,意味着登录成功了.登录成功之后首先必须进入这个页面(http://jwgl.btbu.edu.cn/Logon.do?method=logonBySSO),才能进行其他操作,不然提示未登陆.我们把这个叫获取权限.获取到权限之后我们就可以开始查课表或者查成绩了.

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

Runnable kebiaoRunnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try{
yzmBitmap = GET("http://jwgl.btbu.edu.cn/verifycode.servlet");
gengxin("获取验证码完毕");
EditText numEditText = (EditText)findViewById(R.id.changpao_num2);
EditText pswEditText = (EditText)findViewById(R.id.changpao_psw2);
yzmBitmap=im2bw(yzmBitmap);
String yzm = shibie(yzmBitmap);
gengxin(yzm);
String resultString = POST("http://jwgl.btbu.edu.cn/Logon.do","method=logon&USERNAME="+numEditText.getText()+"&PASSWORD="+pswEditText.getText()+
"&RANDOMCODE="+yzm);
//System.out.println(resultString);
if(dialog2.isShowing())dialog2.dismiss();
if(find(resultString, "http://jwgl.btbu.edu.cn/framework/main.jsp"))
{
gengxin("登录成功,获取权限……");
POST("http://jwgl.btbu.edu.cn/Logon.do?method=logonBySSO","");
//登录成功
loadkebiao();
}else if(find(resultString,"验证码错误"))
{
gengxin("验证码错误,重新登录……");
run();
}
else if(find(resultString, "errorinfo"))
{
if(dialog2.isShowing())dialog2.dismiss();
show(zhongjian(resultString, "errorinfo">","</span>")+"n默认密码为学号或身份证后六位。");
}else show("登录失败");
}catch(Exception e)
{
if(dialog2.isShowing())dialog2.dismiss();
}
if(dialog2.isShowing())dialog2.dismiss();
}
};

上面是查课表之前做的操作.

查课表

终于到查课表了,首先我们需要获取的是学期,学期格式大致是2014-2015-1. 有了学期之后,我们POST这样的数据就可以查到课表: http://jwgl.btbu.edu.cn/tkglAction.do?method=goListKbByXs&sql=&xnxqh=你要查的学期 就象这样

1
2

result = POST("http://jwgl.btbu.edu.cn/tkglAction.do?method=goListKbByXs&sql=&xnxqh="+xueqi, "");

你得到的数据通常是十分复杂的,所以我们就要引入一个十分有用的东西,它叫正则表达式(Regex),教程参考这个链接,之后我们的数据处理都要使用其中的工具.

这里我先贴一张图,感受一下正则表达式的威力.

我们可以看到,正则表达式威力巨大,上图是提取课程名称,上课老师和教师在哪的.有人问,时间呢?这里面没有时间..时间是这个正则表达式提取的:<div id=“(.+?)-2”.*?>(.+?)

让我们再上一张图:

我们看到了1-1,1-2,1-3这样的id,这个就是表示第几周的第几节课.我们取出来上课时间之后,再把右边的数据用刚才的正则表达式取一遍,就可以取出来课程名称,上课地点等数据了.然后就可以用程序画一个课程表了.

这是我们第一个课表,十分简陋,经过各方面考虑和调整,我们取消了左边的时间,然后将有单双周这样的特殊课程用颜色标出来,然后针对课程名称短的方格进行扩大处理,保证了整齐性,下面就是经过多方面调整的课程表:

像这样的课表,我们从一个网页,获得它中间有用的数据,到显示出来,虽然是一瞬间,但是非常的复杂,下面是从数据源到数组的程序(只有变成数组才能显得好看,没有人愿意看

这样的东西).

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

try{
p=Pattern.compile("<div id="(.+?)-2".*?>(.+?)</div>");
m=p.matcher(result);
String temp;
while(m.find()){
shijian.add(m.group(1));
temp=m.group(2);
Pattern p2=Pattern.compile(" (.*?)<br>(.+?)<br>(.*?)<br><nobr> *(.*?)<nobr><br>(.*?)<br>(.*?)<br>");
Matcher m2=p2.matcher(temp);
if(m2.find())
{
mingcheng.add(m2.group(1));
// int i;String myString="";
// for(i=1;i<6;i++)if(m2.group(i).length()!=0)myString+=m2.group(i)+"n";

// xiangxi.add(myString.substring(0,myString.length()-1));
temp=temp.replace(" ","");
System.out.println(temp);
Pattern p3=Pattern.compile("<.+?> *");
Matcher m3=p3.matcher(temp);
temp=m3.replaceAll("n");
while(temp.indexOf("nn")!=-1)
{
temp=temp.replace("nn","n");
}
temp=temp.substring(0,temp.length()-1);
xiangxi.add(temp);

String[] tempStrings = temp.split("n");

if(tempStrings.length==5)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
myString+=tempStrings[4];
didian.add(myString);
}
else if(tempStrings.length==6)
{
if(tempStrings[4].indexOf("暂无")==-1)didian.add(tempStrings[4]+"n"+tempStrings[5]);
else didian.add(tempStrings[5]);
}
else if(tempStrings.length==10)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[3]+"n";
myString+=tempStrings[4]+"n";

if(tempStrings[8].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[8].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[8]+"n";
myString+=tempStrings[9];

didian.add(myString);
}
else if(tempStrings.length==12)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[3];
myString+=tempStrings[4]+tempStrings[5]+"n";

if(tempStrings[9].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[9].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[9];
myString+=tempStrings[10]+tempStrings[11];
didian.add(myString);
}

else {
didian.add(tempStrings[4]);
}
}else
{
mingcheng.add("");
xiangxi.add("");
didian.add("");
}
}
}catch(Exception e)
{}

做完了这个之后,我们觉得这个功能的应用场景应该是早上快迟到了,在文二楼找教室又忘记了在哪上课,掏出手机查课表的情况,于是乎我们想到,那时候肯定没网啊,于是离线课表做出来了.

其实说了这么久,你还不知道怎么选学期对不对?选学期呢,我们是通过这个方式来取出学期的(其实就是教务管理系统选学期的页面)

1
2
3
4
5
6

System.out.println("开始选学期");
result = POST("http://jwgl.btbu.edu.cn/tkglAction.do?method=kbxxXs", "");
p=Pattern.compile("<option value=".*?".*?>(.*?)</option>");
m=p.matcher(result);m.find();
while(m.find()){xueqiList.add(m.group(1));}

这里我们再一次发挥正则表达式的威力,很显然用正则表达式是十分方便的. 我们取出来了学期之后发现,十年都有了…可是大学才四年对不对!! 下面就是教务管理里面的选学期部分.

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

<select name="xnxqh" id="xnxqh" style="width:100px" size=1>
<option value="">---请选择---</option>
<option value="2014-2015-1" selected>2014-2015-1</option>
<option value="2013-2014-2">2013-2014-2</option>
<option value="2013-2014-1">2013-2014-1</option>
<option value="2012-2013-2">2012-2013-2</option>
<option value="2012-2013-1">2012-2013-1</option>
<option value="2011-2012-2">2011-2012-2</option>
<option value="2011-2012-1">2011-2012-1</option>
<option value="2010-2011-2">2010-2011-2</option>
<option value="2010-2011-1">2010-2011-1</option>
<option value="2009-2010-2">2009-2010-2</option>
<option value="2009-2010-1">2009-2010-1</option>
<option value="2008-2009-2">2008-2009-2</option>
<option value="2008-2009-1">2008-2009-1</option>
<option value="2007-2008-2">2007-2008-2</option>
<option value="2007-2008-1">2007-2008-1</option>
<option value="2006-2007-2">2006-2007-2</option>
<option value="2006-2007-1">2006-2007-1</option>
<option value="2005-2006-2">2005-2006-2</option>
<option value="2005-2006-1">2005-2006-1</option>
<option value="2004-2005-2">2004-2005-2</option>
<option value="2004-2005-1">2004-2005-1</option>
<option value="2003-2004-2">2003-2004-2</option>
<option value="2003-2004-1">2003-2004-1</option>
<option value="2002-2003-2">2002-2003-2</option>
<option value="2002-2003-1">2002-2003-1</option>
<option value="2001-2002-2">2001-2002-2</option>
<option value="2001-2002-1">2001-2002-1</option>
<option value="2000-2001-2">2000-2001-2</option>
<option value="2000-2001-1">2000-2001-1</option>
<option value="1999-2000-2">1999-2000-2</option>
</select>

于是乎我们从学号的前两位入手,你最多只能上到你入学那个年份的学期啊!(比如13XXXXXXXX第一个学期是2013-2014-1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14

List<String> xueqiList2 = new ArrayList<String>();
SharedPreferences sp = getSharedPreferences("data", 0);
String num = sp.getString("num_jiaowu", null);
num=num.substring(0,2);
int i;
for(i=0;i<xueqiList.size();i++)
{
String temp = xueqiList.get(i).toString();
xueqiList2.add(temp);
if(temp.indexOf(num)!=-1&(Integer.valueOf(temp.substring(temp.length()-1,temp.length()))==1))break;
}
String[] xueqiStrings = new String[xueqiList2.size()];
xueqiList2.toArray(xueqiStrings);

这里我们取出来了num的前两位,然后xueqiList循环取学期,出现了13且最后一位是1的话我们就break了(其实就是在找2013-2014-1) 这样我们的学期问题就给彻底解决了而且在学期末还可以查下个学期的课表

比如上图…就是我下个学期的课表.

然后原始课表这个不起眼的小按钮是打开了教务管理那个丑的原始页面.为什么要留这个功能呢?因为有周末要上课的同学反馈查不到周末的课表,但是我这里排版已经这样了,再压缩就显示不出来了,于是留下了原始课表这个功能.