人到大三、也不想考研。在留在大学有限的时间里,想做一些有意义的事情。第一个想法是开发一个应用,可以查课表、成绩、空教室等等,毕竟在我的学校大多数人使用的超级课程表,面对那么多用户,那么多高校的教务系统,所以难免会有些高延迟。如果我们缩小用户范围,仅仅面对我们学校,那速度肯定会快很多的,但是询问过学校的信息中心,发现高校用的教务系统是外包给外面的公司管理的,并没有API接口。于是首要任务变成了提供API接口。
很显然,教务官网没有提供API接口给我们,于是只能自己模拟登陆写爬虫了。在搜索引擎上进行搜索,发现有类似想法的人还是很多的,但是我逛了一大圈、居然只有Java、Python两个版本,没有别的版本了,但好在思路很清晰。
为了后期开发方便,提供接口,我用JavaScript重构一下。我相信开发网站的全干工程师们,比起Java和Python,后端用Nodejs会更加顺手一点吧,也在此文中记录一些踩过的坑。
虽然网络上已经对爬取的过程有了原理性的介绍了,但为了避免读者还要反复阅读别的文章的麻烦、这里还是要详细的介绍一下流程。
本次开发使用的环境
操作系统:MacOS
开发框架:Egg.js(阿里为了规范提出的框架, 熟悉nodejs的es6语法和koa2框架的同学一个多小时就可以上手)
浏览器和抓包工具:Chrome/Safari、Charles
开源仓库地址:戳我前往
模拟登陆
第一步、分析登陆表单
先贴出本次实验的正方教务系统的界面,省的不同的看官白费力气,但就算界面不同,思路肯定也是相同的,看看也无妨。
按F12打开开发者工具,调到Network这栏(不知道为啥写文章的时候我的chrome有点抽风,所以我打开了charles,该工具类似于Windows上的Fiddler),我们首先模拟一次失败的登陆,我这里键入虚假的用户名和密码:1543140220/123456。
前端给后端发了两个请求,我们来逐一分析。
第一个请求
其实从请求的名字就能看得出来,是获取加密的公钥的,可以大胆的猜测这是一个RSA加密了,等会儿我们分析js代码的时候会证明这一点,所以我们要记住modulus和exponent这两个参数。
第二个请求
第二个请求携带的参数就多了
csrftoken:这个我们一般是用来防止网站遭受xss跨站请求脚本攻击的时候弄的玩意
如何获取这个令牌呢?
搜索一下网页的html可以发现,这玩意藏在表单的一个隐藏标签里。在我的项目中,使用了cheerio这个库来解析HTML。相关代码如下:
async get_csrf_token(session, time) {
const ctx = this.ctx;
let headers = {
'Host': 'jwgl.njtech.edu.cn',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0',
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Connection': 'keep-alive',
'Cookie': session
}
const options = {
headers,
}
const url = await this.service.common.get_login_url();
const res = await ctx.curl(url + time, options);
const resultHtml = (res.data.toString(