新版正方教务系统请点这里:模拟登陆新版正方教务管理系统(获取学籍信息、课表和成绩)
最近想学点爬虫玩玩,拿学校的教务系统练练手。学校与很多高校一样,用的是正方教务管理系统,非常的不好用,经常出现登陆不上去、卡死的情况,主页如下图所示:
主页地址:http://222.24.62.120
模拟登录
1. 分析登录的URL和所需提供的数据
我们输入学号、密码和验证码登录后,点击登录。这时浏览器会向服务器提交一个POST请求:
我们由上图中的数据可知,登录请求的URL地址为:
http://222.24.62.120/default2.aspx
所提交的数据除了学号、密码、验证码、用户类型,还有其他的数据:
- __VIEWSTATE在源码中可以找到,是一个隐藏域,猜测是用来做验证
<input type="hidden" name="__VIEWSTATE" value="dDwxNTMxMDk5Mzc0Ozs+lYSKnsl/mKGQ7CKkWFJpv0btUa8=" />
- Textbox1是上次登陆的用户学号
- RadioButtonList1通过看源码可知为%D1%A7%C9%FA,是”学生”经过URL编码(gb2312)后的字符串。
- 后四个为空可以不用管
创建一个类:ConnectJWGL
属性如下:
private String stuNum;
private String stuName;
private String __VIEWSTATE = "";
private Map<String,String> cookies = new HashMap<>();
private Connection connection;
private Connection.Response response;
private Document document;
2. 获取Cookies和__VIEWSTATE
给主页发一个请求,然后将响应的Cookies保存下来。并在其中提取__VIEWSTATE的值。
在ConnectJWGL类中添加一个connectIndex方法
代码如下:
//接获取cookies和__VIEWSTATE
public void connectIndex(){
try{
//获取连接
connection = Jsoup.connect("http://222.24.62.120");
connection.header("User-Agent",// 配置模拟浏览器
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0");
response = connection.timeout(3000).execute();
//保存Cookies
cookies = response.cookies();
// 将响应转换为Dom树,以便获取__VIEWSTATE
document = Jsoup.parse(response.body());
for(Element element:document.getElementsByTag("input")) {
if (element.attr("name").equals("__VIEWSTATE")) {
__VIEWSTATE = element.val();
break;
}
}
}catch (IOException ex){
ex.printStackTrace();
}
}
3. 取得验证码
在Chrome中定位验证码图片,代码如下
<img id="icode" title="看不清,换一张" onclick="reloadcode();" alt="看不清,换一张" src="CheckCode.aspx" border="0" style="POSITION: absolute; TOP: 5px; LEFT: 130px">
可以知道点击图片就会重新申请一个验证码,src为:
http://222.24.62.120/CheckCode.aspx
图片处理的方式有很多种,因为ORC图片识别成功概率低、机器学习学习难度比较大,所以采用把图片下载到本地再手动输入的方法,以后我倾向于自己写一个验证码识别工具。
在获取验证码的时候,要将Cookies上传,让服务器知道你的身份,要不然下载下来的图片没法用。
在ConnectJWGL类中添加一个downloadCheckcode方法
代码如下:
//下载验证码到本地
public void downloadCheckcode(){
try{
String captcha_url = "http://222.24.62.120/CheckCode.aspx";
response = Jsoup.connect(captcha_url)
.cookies(cookies).ignoreContentType(true) // 获取图片需设置忽略内容类型
.userAgent("Mozilla").method(Connection.Method.GET).timeout(3000).execute();
byte[] bytes = response.bodyAsBytes();
//在本地建立文件夹
File file = new File("/home/limeng/Desktop/checkcode.aspx");
if (file.exists()) {
file.delete();
}
OutputStream output = new FileOutputStream(file);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
bufferedOutput.write(bytes);
bufferedOutput.close();
output.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
4. 登录
在ConnectJWGL类中添加一个login方法
//登录
public boolean login(String stuNum,String password,String checkCode){
this.stuNum = stuNum;
stuName = "";
//填充post数据
Map<String, String> datas = new HashMap<>();
datas.put("__VIEWSTATE",__VIEWSTATE);
datas.put("txtUserName",stuNum);
datas.put("TextBox2",password);
datas.put("txtSecretCode",checkCode);
datas.put("RadioButtonList1","%D1%A7%C9%FA");
datas.put("Button1","");
datas.put("lbLanguage","");
datas.put("hidPdrs","");
datas.put("hidsc","");
try{
connection = Jsoup.connect("http://222.24.62.120/default2.aspx");
connection.header("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0");
// 设置cookie和post上面的map数据
response = connection.postDataCharset("GB2312").ignoreContentType(true).method(Connection.Method.POST)
.data(datas).cookies(cookies).execute();
document = response.parse();
if(document.title().equals("欢迎使用正方教务管理系统!请登录")){
String error = document.getElementsByTag("script").get(1).data();
Pattern pattern = Pattern.compile("(?<=alert\\(')[^']*");
Matcher matcher = pattern.matcher(error);
while(matcher.find()){
System.out.print("登陆失败,");
System.out.println(matcher.group());
}
return false;
}else {
stuName = document.getElementById("xhxm").text();
stuName = stuName.substring(0,stuName.length()-2);
System.out.println("登陆成功,欢迎你"+stuName+"!");
return true;
}
}catch (IOException ex){
ex.printStackTrace();
return false;
}
}
登录成功会跳转到:
http://222.24.62.120/xs_main.aspx?xh=学号
否则转到登录界面,并提示错误信息。
获取学生个人信息
在登录成功后,我们点击信息维护中的(个人信息)。
这时会去以get方式请求:
http://222.24.62.120/xsgrxx.aspx
传递了三个参数:
- xh:学号
- xm:URL编码过的姓名
- gnmkdm:N121501(应该是业务编号,请求课表就是N121603)
我们不知道学生的姓名,但是通过查看主界面,可以发现“欢迎您: ××同学”,简单处理一下即可。
Referer(很重要,要不然无法请求上面的连接)
http://222.24.62.120/xs_main.aspx?xh=学号
在ConnectJWGL类中添加一个getStudentInformation方法
代码如下:
//获取学生个人信息
public void getStudentInformation(){
try{
String infoURL = "http://222.24.62.120/xsgrxx.aspx?xh="+stuNum+"&xm="+URLEncoder.encode(stuName,"GB2312")+"&gnmkdm=N121501";
connection = Jsoup.connect(infoURL);
response = connection.ignoreContentType(true).method(Connection.Method.GET)
.userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0")
.referrer("http://222.24.62.120/xs_main.aspx?xh="+stuNum)
.cookies(cookies).postDataCharset("GB2312").timeout(3000).execute();
document = response.parse();
if(document.getElementById("lbxsgrxx_xb") != null){
System.out.println("---基本信息---");
System.out.println(document.getElementById("lbxsgrxx_xb").text()+document.getElementById("lbl_xb").text());
System.out.println(document.getElementById("lbxsgrxx_csrq").text()+document.getElementById("lbl_csrq").text());
System.out.println(document.getElementById("lbxsgrxx_xy").text()+document.getElementById("lbl_xy").text());
System.out.println(document.getElementById("lbxsgrxx_dqszj").text()+document.getElementById("lbl_dqszj").text());
System.out.println(document.getElementById("TDzymc").text()+document.getElementById("lbl_zymc").text());
System.out.println(document.getElementById("lbxsgrxx_xzb").text()+document.getElementById("lbl_xzb").text());
}else {
System.out.println("获取信息失败");
}
}catch (IOException ex){
ex.printStackTrace();
}
}
测试
Main.java
import java.util.Scanner;
public class Main {
public static void main(String[] args){
String stuNum;
String password;
String checkCode;
Scanner input = new Scanner(System.in);
ConnectJWGL connectJWGL = new ConnectJWGL();
connectJWGL.downloadCheckcode();
System.out.print("请输入学号:");
stuNum = input.next();
System.out.print("请输入密码:");
password = input.next();
System.out.print("请输入验证码:");
checkCode = input.next();
if(connectJWGL.login(stuNum,password,checkCode)){
connectJWGL.getStudentInformation();
}
input.close();
}
}
完整代码点这里:爬取学校教务系统获取学生信息
要注意的细节
- 在获取Cookies后,以后的每一次请求都要把Cookies带上。
- Too many redirects occurred trying to load URL 表示进入重定向循环,不知道该跳转到哪一个界面。如果把自动跳转关闭就出现页面提示:Object moved to here.
这个问题是在获取个人信息时出现的,由于在登录成功时不小心更新了一次Cookies,导致Cookies为空,请求时服务器不知道我的身份,修正后就可以正常获取信息了。 - 请求时要注意目标请求是否需要Referer。Referer告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理,有网页会限定请求的上一个地址。