最近都在搞和爬虫相关的东西,在搞完学校新闻模拟登录后,就感觉有种一丢丢的成就感,所以心血来潮想自己弄一个教务系统出来。在之前实现模拟登陆的时候本人无法通过HttpWatch进行分析,因为登陆界面是在外网的时候才会出现,所以但是就用手机把网页下载下来进行分析,然后找出需要post的参数,然后实现模拟登录。按照这样的思路,无疑我会用这种思路去实现登录正方系统,但是结果尝试很多次都不行。结果通过HttpWatch观察发现post的时候还有一个隐藏的参数,所以之前才会行不通.
以下是效果图:
实现以上的各种功能其实关键在于把协议给弄清楚。首先需要我们注意的是登录需要帐号,密码和验证码,而通过httpwatch不难发现,其实我们访问整个过程中我们都需要用到我们验证码所带的Cookie。换句话来说,我们要在获取到验证码图片的时候,获取到我们需要的Cookie信息。
以下是HttpWatch抓到验证码的信息:
通过获取验证码,我们会将cookie保存下来,保存下来有什么用?看看下面这图就知道了
以下是我登录正方系统后所抓到的信息:
我们可以看到我们请求登录的时候请求头所带的cookie正是我们刚刚获取到验证码的时候所带的cookie,这就是为什么我们获取到验证码时要保存cookie的原因。
接着我们再来看一下这个post请求里面的信息,里面有__ViewStat(我也不知道是什么,不过一起带着Post就好了)RadioButtonList1(单选按钮:学生...),txtUserName:账号,TextBox2:密码,txtSecretCode:验证码。当然RadioButtonList1后面乱码了我们不知道传什么东西,不过淡定,我们在Stream中可以看到有一个_VIEWSTATE,而这个key所对应的值正是我们所传的所有参数的一个字符串。
__VIEWSTATE=dDwyODE2NTM0OTg7Oz6IMxu8wRHiZevFvdItT8RVFJjAFQ%3D%3D&txtUserName=yournumber&TextBox2=yourkey&txtSecretCode=ctd3&RadioButtonList1=%D1%A7%C9%FA&Button1=&lbLanguage=&hidPdrs=&hidsc=
看吧,根据Stream中的值我们就可以知道RadioButtonList1的值。
说了那么多我们就直接亮出我们的代码把。
获取验证码:
/**
* 验证码功能
*
*/
public static Bitmap getCode()
throws UnsupportedOperationException, Exception {
// 获取验证码
HttpGet secretCodeGet = new HttpGet(secretCodeUrl);
client = new DefaultHttpClient();
HttpResponse responseSecret = client.execute(secretCodeGet);
// 获取返回的Cookie
Cookie = responseSecret.getFirstHeader("Set-Cookie").getValue();
viewState = IOUtils.getViewState(indexUrl, "", "");
InputStream content = responseSecret.getEntity().getContent();
final Bitmap bitmap = BitmapFactory.decodeStream(content);
// client=null;不能执行这个代码因为登录的时候还要用到这个client
return bitmap;
}
当然我们需要获取到_VIEWSTATE:
/**
* 获取隐藏字段的__VIEWSTATE值
*
* @param url
* @param cookie
* @param referer
* @return
* @throws UnsupportedOperationException
* @throws ClientProtocolException
* @throws IOException
*/
public static String getViewState(String url, String cookie, String referer)
throws UnsupportedOperationException, ClientProtocolException,
IOException {
HttpClient client = new DefaultHttpClient();
HttpGet getViewState = new HttpGet(url);
getViewState.setHeader("Cookie", cookie);
getViewState.setHeader("Referer", referer);// 设置头信息
String s = IOUtils.getHtml(client.execute(getViewState).getEntity()
.getContent(), "GB2312");
String viewstate = Jsoup.parse(s).select("input[name=__VIEWSTATE]")
.val();
return viewstate;
}
以上的代码就可以获取到验证码以及对应的Cookie了。
下面我们实现登录的逻辑,按照上面的推理,我们要知道我们要传的参数就是我们下面的参数,记得要带cookie
以下是实现登录功能的代码:
/**
* 登录功能
* @param stuNumber
* @param password
* @param secret
* @return
* @throws ClientProtocolException
* @throws IOException
*/
public static boolean loginUser(String stusNumber, String password,String secret) throws ClientProtocolException, IOException
{
stuNumber=stusNumber;
HttpPost loginPost = new HttpPost(loginUrl);// 创建登录的Post请求
// 阻止自动重定向,目的是获取第一个ResponseHeader的Cookie和Location
loginPost.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
loginPost.setHeader("Cookie", Cookie);// 带上第一次请求的Cookie
List nameValuePairLogin = new ArrayList();// 封装Post提交参数
nameValuePairLogin
.add(new BasicNameValuePair("__VIEWSTATE", viewState));// 隐藏表单值
nameValuePairLogin
.add(new BasicNameValuePair("txtUserName", stuNumber));// 学号
nameValuePairLogin.add(new BasicNameValuePair("TextBox2", password));// 密码
nameValuePairLogin.add(new BasicNameValuePair("txtSecretCode", secret));// 验证码
nameValuePairLogin.add(new BasicNameValuePair("RadioButtonList1",
identityStu));// 身份,默认学生
nameValuePairLogin.add(new BasicNameValuePair("Button1", ""));
nameValuePairLogin.add(new BasicNameValuePair("lbLanguage", ""));
nameValuePairLogin.add(new BasicNameValuePair("hidPdrs", ""));
nameValuePairLogin.add(new BasicNameValuePair("hidsc", ""));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
nameValuePairLogin, "GB2312");
loginPost.setEntity(entity);
HttpResponse responseLogin = client.execute(loginPost);
// 第三步:判断提交数据是否成功,成功返回302
if (responseLogin.getStatusLine().getStatusCode() == 302) {
// 如果提交成功,带着Cookie请求重定向的main页面,并获取学生姓名
HttpGet mainGet = new HttpGet(mainUrl + stuNumber);
mainGet.setHeader("Cookie", Cookie);
mainGet.setHeader("Referer", loginUrl);
HttpResponse responseMain = client.execute(mainGet);
InputStream is = responseMain.getEntity().getContent();
String html = "";
try {
html = IOUtils.getHtml(is, "GB2312");
// System.out.println(html);
} catch (Exception e) {
System.out.println("解析html失败!");
e.printStackTrace();
}
stuName = Jsoup.parse(html).getElementById("xhxm").text();
System.out.println("登录成功!欢迎您:" + stuName);
client=null;
return true;
} else {
System.out.println("登录失败!");
return false;
}
}
以上两部分代码就完美的实现了登录功能。
虽然看起来很简单,但是实际上这可能是这个app中最重要的一个部分。这个分析不仅仅用于这个app也可以用于其他网站的登录分析,比如开源中国,CSDN等,如果有兴趣都可以自己弄一个属于自己的一个客户端出来。
今天就先介绍登录的部分,下次再介绍一下如何获取课表,成绩,以及空课室查询