爬取学校教务网的课表与成绩java版
这是安卓课设时本人做的一个简单的app里面所涉及到的爬虫部分,由于android编程是用java语言来写的,为了兼容,本人也使用了java来写爬虫。
cookie
cookie 在存储上是一段键值对信息,服务器本身无法判断用户身份,cookie便是用来让服务器鉴别身份状态的。在这里服务器就会根据cookie的内容来辨别用户有没有登录,如果我们想获取我们自己的教务网信息,我们只需要拿到能够标识自己身份的cookie和请求放一起提交到服务端,便能拿到自己的信息。
请求头中的 Referer
referer 是 Http 请求header的一部分,它一般标识从哪一个地址发出的本次请求,我们可以通过Referer来找到发出本次的请求来进行分析
Jsoup
一种java第三方包,用来解析HTML,对于数据爬取有很大的帮助
寻找自己的身份标识 cookie(从后向前)
登陆成功的主界面,通过检查 Network,我们找到了里面两个我们比较关心的数据 referer 和 cookie(此时的cookie已经可以标识我们的身份了)
可以注意到cookie 里面有两个jsessionid。但我们并不知道原因,我们便将注意力先放在Referer上。
访问 referer 网址,再次进行检查时,我们再次找到了cookie,对比上面的cookie,我们可以发现这次的cookie比之前的少了一个 jsessionid,我们便可以大胆的猜测,这个缺失的jsessionid便是对身份标识的关键。
试图获取关键的jsessionid
既然是表示身份的jsessionid,那必定与输入的密码与账户有关,我们现在可以看一下登录时发生了什么,下面为点击登录的函数,遗憾的是并没有发现什么有用的线索。
我们现在变换思路从前往后看,我们检查一下NetWork从登录界面到主界面传过来的doc文档,发现前两个doc是第一步我们检查没有发现的界面
我们观察到第一个doc的cookie并没有标识身份的jsessionid
第二个doc却出现了身份标识的jsessionid,并且Referer是第一个doc,我们可以猜测肯定是第二个doc从第一个里面获取到了什么数据。
再次观察第一个doc,发现里面有form表单,里面有三个数据,用户名,密码以及encoded,传过去的密码为空,账户为明码,这两个迹象表明传过去的encoded才是真正的有用的东西,可能是通过加密将密码账户加密成encoded,我们现在就可以开始找endcoded相关的东西了
通过搜索encoded 我们很快的找到了一个比较可疑的函数,这是一个局部刷新的函数,大致意思是向一个网址请求数据,返回的数据再进行一系列操作。其中,里面用到了我们所输入的账户以及密码。并进行了一系列的加密。
通过对loginajax()函数的搜索,发现这里原来就是login()函数所调用的一个函数,之前没有仔细的观察,尴尬了QAQ。现在我们可以确定上面的加密函数确实是对密码账户以及对strUrl进行访问获取的一段string,共同加密得到的encoded。
获得jsessionid
当我们获得endcoded 之后我们便可以访问第二个doc,通过观察,我们可以发现第一个doc中的location便是第二个doc的url,这样就好办了,我们这样通过访问第二个doc便可以获得那个可以真正标识我们身份的cookie了。
通过获取到的cookies我们便可以对学校教务网的任意一个网页进行访问,结合Jsoup来对数据进行爬取!
获取cookie:
package spiderMethod;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class getCookies {
public static String LOGIN_URL = "http://kdjw.hnust.edu.cn/";
public static String url1 = "http://kdjw.hnust.edu.cn/Logon.do?method=logon&flag=sess";
public static String url2 = "http://kdjw.hnust.edu.cn/Logon.do?method=logon";
public static String url3 = "http://kdjw.hnust.edu.cn/jsxsd/xskb/xskb_list.do";
public static String USER_AGENT = "User-Agent";
public static String USER_AGENT_VALUE = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36";
public static String user;
public static String password;
public static Map<String, String> cookies = new HashMap<>();
public static int flag; // 标志是否拿取成功,可用于登录密码是否正确,也可直接判断cookies的值
public getCookies(String user, String password) {
this.user = user;
this.password = password;
}
public int getFlag() {
return flag;
}
public Map getCookies() throws IOException {
return log(user, password);
}
//获得dataString
public static String get_dataString(Connection.Response response) throws IOException {
String s = response.header("Set-Cookie"); //获得Set-Cookie
String[] array = s.split(";| |,|=|/");
// System.out.println(s);
String JSESSIONID = "", SERVERID = "";
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
if (array[i].equals("SERVERID")) {
SERVERID = array[i + 1];
i++;
}
if (array[i].equals("JSESSIONID")) {
JSESSIONID = array[i + 1];
i++;
}
}
//存入新的cookies中
cookies.put("JSESSIONID", JSESSIONID);
cookies.put("SERVERID", SERVERID);
System.out.println(cookies);
Connection connection = Jsoup.connect(url1);
connection.header(USER_AGENT, USER_AGENT_VALUE);
Connection.Response rs = connection.cookies(cookies).execute();
Document doc = Jsoup.parse(rs.body());
System.out.println(doc.select("body").text());
return doc.select("body").text();
}
public static Map log(String user, String password) throws IOException {
try {
Connection connection1 = Jsoup.connect(LOGIN_URL);
//print(LOGIN_URL);
connection1.header(USER_AGENT, USER_AGENT_VALUE); // 配置模拟浏览器
Connection.Response rs = connection1.execute(); // 获取响应
String dataString = get_dataString(rs);
String encoded = get_code.get_encoded(dataString, user, password);
Document d1 = Jsoup.parse(rs.body()); // 通过Jsoup将返回信息转换为Dom树
List<Element> eleList = d1.select("form"); // 获取提交form表单,可以通过查看页面源码代码得知
// 获取cooking和表单属性
Map<String, String> datas = new HashMap<>();
for (int i = 0; i < eleList.size(); i++) {
for (Element e : eleList.get(i).getAllElements()) {
// 设置用户名
if (e.attr("name").equals("userAccount")) {
e.attr("value", user);
}
// 设置用户密码
if (e.attr("name").equals("userPassword")) {
e.attr("value", "");
}
if (e.attr("name").equals("encoded")) {
e.attr("value", encoded);
}
// 排除空值表单属性
if (e.attr("name").length() > 0 && !e.attr("name").equals("loginForm")) {
datas.put(e.attr("name"), e.attr("value"));
}
}
}
Connection con2 = Jsoup.connect(url2);
con2.header(USER_AGENT, USER_AGENT_VALUE);
System.out.println(cookies);
// 设置cookie和post上面的map数据
Connection.Response login = con2.followRedirects(false)
.method(Connection.Method.POST)
.data(datas).cookies(cookies)
.header("ContentType", "application/x-www-form-urlencoded")
.execute();
// System.out.println(login.body());
// System.out.println(login.cookies());
String location = login.header("Location");//空
System.out.println(location);
Connection connection3 = Jsoup.connect(location);
Connection.Response cdx = connection3.followRedirects(false)
.method(Connection.Method.GET)
.header("ContentType", "application/x-www-form-urlencoded")
.cookies(cookies)
.execute();
// 打印,登陆成功后的信息
if (cdx.statusCode() != 302)
System.out.println("登陆失败");
else {
System.out.println(cdx.cookies());
Map<String, String> map = cdx.cookies();
for (String s : map.keySet()) {
System.out.println(s + " : " + map.get(s));
}
Connection con7 = Jsoup.connect(url3);
cdx.cookies().put("SERVERID", cookies.get("SERVERID"));
}
System.out.println(cdx.cookies());
flag = 1;
return cdx.cookies();
} catch (Exception e) {
Map<String, String> st = new HashMap<String, String>();
flag = 0;
return st;
}
}
}
这里给出获取cookie 的步骤,加密函数就不给出了哈~
我把具体代码放在GitHub上了,大家有兴趣自取
里面包含了对自己四六级,课程成绩,考试安排等方面的爬取,欢迎大家学习交流
下面给出jsoup以及其他的依赖包: