闲来无事,想要爬取一下成绩课表等东西,所以目标就是教务处网站
我的配置是:windows10,IDEA,谷歌浏览器
整个项目是基于maven的,主要用到了两个包
HttpClient以及Jsoup
HttpClent主要用来模拟访问的
Jsoup主要用来分析数据的。
pom.xml
<dependencies>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.9version>
dependency>
<dependency>
<groupId>com.github.jjYBdx4IL.utilsgroupId>
<artifactId>jsoup-utilsartifactId>
<version>1.0version>
dependency>
dependencies>
首先我们需要模拟登录,然后基于登录的状态去访问课表成绩等。
我们主要用谷歌浏览器,对我们要进行访问的url进行分析,
F12 打开开发者窗口,
我们必须找到登录的url(界面展示),肯定是越简单越好
以我们学校举例:https://cas.hrbeu.edu.cn/cas/login
一般都是第一个就是我们要仔细分析的
观察图片的几个组成部分,左边是一个一个的发送的请求,点一下左边的某一个请求就有了右边的详情,
右边的header也由多部分组成
general 表示基本的情况,可以发现第一个url就是我们通常要找的url,方法也要特别注意。
responds就是回复的消息,这里我们注意我打码的部分就是对面返回的cookie,我们之后的访问也会依靠这个cookie来模拟我们是登录的。
这是下半部分
request就是我们对服务器发送的请求,需要注意的是cookie,它告诉我们:我们在发送登录请求的时候是附带了一个cookie的信息的。这里的cookie分为两部分,一部分为jsessionid,一部分为message_ticket。我们需要找到他们是从哪来的,是否为固定值。
form data 就是我们发送的表格信息了,注意不要遗漏任何一个我们发送过去的数据。这里注意仍然有一个值比较特殊就是lt,我们仍然需要找到它是从哪出现的。
我们很容易猜测到这些值肯定是我们第一次打开登录界面的时候存在的值,我们就需要寻找他们在哪。
对于cookie来讲,我们重新打开一次未登录的登录界面时,从F12的开发工具中,第一个请求中的cookie中就发现这个jsessionid,经过多次观察message_ticket是一个固定值,写死即可。
对于隐藏的表格值,我们可以用Ctrl+u打开源码,寻找对应的name属性一致的地方,发现:lt每次都不一样,所以我们需要先得到这个值,而其他为固定值,
刚才已经理清了思路,我们需要第一次访问(get)登录界面,得到jsessionid以及lt
// 创建客户端
CookieStore httpCookieStore = new BasicCookieStore();
//后面这个创建的函数最后给
CloseableHttpClient client = SpriderUtils.createHttpClientWithNoSsl(httpCookieStore);
/* 第一次请求[GET] 拉取流水号信息 */
HttpGet request = new HttpGet("https://cas.hrbeu.edu.cn/cas/login");
//执行请求
HttpResponse response = client.execute(request);
//从cookie中得到并裁剪出jsessionid
Header headerSetCookie = response.getFirstHeader("Set-Cookie");
String TGC = headerSetCookie.getValue();
int indexOfJ = TGC.indexOf(";");
String jsessionid = TGC.substring(0,indexOfJ);
//提取lt数据 ,readResponse这个最后给
Document htmlPage = Jsoup.parse(SpriderUtils.readResponse(response));
Element form = htmlPage.select("#fm1").first();
String lt = form.select("[name=lt]").first().val();
//post
HttpPost request2 = new HttpPost("https://cas.hrbeu.edu.cn/cas/login;"+jsessionid);
request2.setHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");
//附加上我们的cookie
request2.setHeader("Cookie",jsessionid+"; MESSAGE_TICKET=%7B%22times%22%3A0%7D");
//写入要post的值(我这里简写了,少几个form参数)
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", password));
params.add(new BasicNameValuePair("captcha", ""));
params.add(new BasicNameValuePair("lt", lt));
request2.setEntity(new UrlEncodedFormEntity(params));
HttpResponse response2 = client.execute(request2);
最后只要我们从response2获取了正确的cookie就说明我们登录成功了
附录的函数(上面的需要这两个)
/**
* 创建客户端
* @param cookieStore
* @return
* @throws Exception
*/
public static CloseableHttpClient createHttpClientWithNoSsl(CookieStore cookieStore) throws Exception {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// don't check
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// don't check
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.setDefaultCookieStore(cookieStore == null ? new BasicCookieStore() : cookieStore)
.build();
}
public static String readResponse(HttpResponse response) throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()));
String result = new String();
String line;
while ((line = in.readLine()) != null) {
result += line;
}
return result;
}
这里思路很简单,带上我们的cookie即jsessionid访问即可
//get请求
HttpGet request3 = new HttpGet("http://edusys.hrbeu.edu.cn/jsxsd/xskb/xskb_list.do");
//写入cookie
request3.setHeader("Cookie",jsessionid);
//执行
HttpResponse response3 = client.execute(request3);
//处理
Document htmlPageNeed = Jsoup.parse(SpriderUtils.readResponse(response3));
System.out.println(htmlPageNeed.toString());
可能是我们教务处的奇怪,访问总是重定向到某一个页面,之后的解决方案为访问两次,第一次会重定向,第二次就正确的界面了。即再次执行execute。
我们可以得到
Document htmlPageNeed = Jsoup.parse(SpriderUtils.readResponse(response3));
对其进行处理,可以发现其有很多函数,查找某个元素也是很方便,
这里想提到的小技巧是我们可以对其进行debug,详细查看其属性,然后根据属性的名字找到对应的方法名字。
很多类都重写了toString可以放心的输出看看。
难得自己的兴趣再加上自己有时间,学起来有意思。
学习了谷歌的开发者工具(F12),不得不说是一个很强大的工具,可以看它就经历了什么过程。
包括其发送的url,以及request和responds知道自己发了什么,而对方发了什么。
同时遇见bug时,我们也可以将responds的header输出一下找找开发者工具中一样的某个请求,分析一下为什么会到这里。
一个很重要的方法就是模拟,我们的代码在模拟我们的访问习惯,当我们遇见bug时,我们也要模拟我们的代码是怎么个流程,走到了哪里。很多时候,出现bug的原因就是我们以为这样的访问顺序会直接得到结果,但按代码的模拟url访问发现结果不对。
我所做的以及我理解的爬虫还是简单的固定url访问,可能高端的可以自动识别url进行爬取,还有一定的局限性。