js爬虫

之前都是用的python爬虫,现在想往web方向发展.
话不多说,nodejs的superagent+cheerio

nodejs是js的环境,除此之外还有浏览器,但我们肯定不在浏览器上嘛.

superagent拿来爬取,在这之前我也尝试过axios,request,事实证明superagent很好用.

cheerio拿来处理爬取到的数据,主要就是html.

目的是某末流二本的教务系统.

我最终目的是获得课程信息以及空闲教室信息.

1.superagent爬取

首先需要登录.

结合多次尝试,获得了的登录url http://us.nwpu.edu.cn/eams/login.action

根据末尾很容易知道需要往这个url发送post,话不多说直接构造post

1
2
3
4
5
6
7
8
9
uid = ''; //学号
pwd = ''; //密码
superagent
.post(login_url)
.type('form') //这个type表示是application/x-www-form-urlencoded
.send({username:uid,password:pwd}) //form的数据 post肯定需要的
.end(function(err,res){
//do somesthing
});

至于为什么类型是application/x-www-form-urlencoded,当然是抓取到的,

image-20211214220056824

经典登录界面,我们猜到了点击登陆之后就会发送一个post.

image-20211214220155928

image-20211214220250378

然后就抓取到了报文,可以看到上面的content-type就是这个.

咳咳,暴露了一点个人信息.

接下来难点来了,这是个302重定向,重定向到了哪里呢?事实上可以看到有了location是home.action

我们就能找到了,其实无脑找找就能找到的,事实上我们使用superagent就能返回重定向的home.action的信息。

这下就登录了.其实并不难(一开始我使用的其他两个工具,重定向导致失败了,我就更换了第三方包)

然后就是重点,登录之后需要cookie,不然就没意义了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function(err,res){
console.log(res);
var cookieid = res.headers['set-cookie'][0].split(';')[0].split('=')[1];
// console.log(cookieid);
var cookie = `semester.id=158; JSESSIONID=${cookieid}; GSESSIONID=${cookieid}`;
superagent.post(info_url)
.type('form')
.send({
ignoreHead: 1,
"setting.kind": "std",
startWeek: 1,
"semester.id": 158,
ids: 248032
}

cookieid就是这个image-20211214220742435

jsession与gsession是一样的,所以获得了一个之后,根据cookie设置就行了.注意格式就是上面这个图片的格式,我们可以直接复制下来,修改一下就行了.

为了获得课程信息,可以根据网页查看是哪个返回的请求,这里我查看的是coursetable.

image-20211214220926672

可以猜到参数的意思,后面两个是学期的信息,我们只需要修改startWeek就能修改周数查看那一周的课程了.

所以只要设置cookie,再send发送表单数据即可获取页面信息,然后还要进行处理,不然不太好看.处理我就不细说了,根据个人喜好,我就直接把课程名弄出来就行了,可以根据自己情况教室啥的都可以.

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const superagent = require("superagent");
var login_url = "http://us.nwpu.edu.cn/eams/login.action";
var info_url = 'http://us.nwpu.edu.cn/eams/courseTableForStd!courseTable.action';
uid = "";//需要自己填写
pwd = "";//
superagent
.post(login_url)
.type('form')
.send({username:uid,password:pwd})
.end(function(err,res){
console.log(res);
var cookieid = res.headers['set-cookie'][0].split(';')[0].split('=')[1];//这一句写的并不好,我对于js的基础还是掌握不够. 这一句主要是想把cookie中的那段代码提取出来.
// console.log(cookieid);
var cookie = `semester.id=158; JSESSIONID=${cookieid}; GSESSIONID=${cookieid}`;//cookie
superagent.post(info_url)//semester.id我不清楚是不是跟个人有关,可以自己抓一下看看.
.type('form')
.send({
ignoreHead: 1,
"setting.kind": "std",
startWeek: 1,
"semester.id": 158,
ids: 248032
})
.set("Cookie",cookie)
.end(function(err,res){
if(res.statusCode == 200)
console.log(res.text);
});
});

最后就能得到页面的html

然后使用cheerio处理

2.cheerio处理

关于数据处理其实有很多方法,最后影响的是性能.

当你看了cheerio的文档,会感叹其的强大功能.

事实上我并没有太仔细地看相关用法,我直接使用了css选择器以及cheerio的一些简单用法.

image-20211214225431353

可以看到table id=”grid..”就是课程的信息,这里我直接使用css选择器基本可以解决问题(事实上很多时候我都在想选择的方法是不是太多了).

直接上代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var courses = [];
var teachers = [];
var weeks = [];
const $ = cheerio.load(res.text);
$("tbody[id] tr td:nth-child(5)").each(function (i,elem) {
teachers[i] = $(this).text();
});

$("tbody[id] tr td:nth-child(9)").each(function (i,elem) {
weeks[i] = $(this).text().trim();
});
$("tbody[id] tr td:nth-child(4)").each(function (i, elem) {
courses[i] = $(this).text();
});

courses.forEach((value,index)=>{
// console.log(value);
value+=" 老师:"+teachers[index]+"上课时间:"+weeks[index]+"周";
console.log(value);
});

我将课程,老师以及上课的周挑选了出来,其实有很多方法,我相信我这种应该是比较傻的.

然后这样登陆以及课程信息都有了.

然后就是查询空闲教室,有了之前的学习,现在意外的轻松.

在那个网页试一下,抓取请求

image-20211215101931605

image-20211215101854884

所以是个post,数据也都能看到.构造就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
superagent
.post(free_url)
.type("form")
.set("Cookie", cookie)
.send({
"classroom.type.id": "", //教室类型
"classroom.campus.id": "", //校区
"classroom.building.id": "", //教学楼id
seats: "", //容量限制
"classroom.name": "", //教室
"cycleTime.cycleCount": 1, //时间周期
"cycleTime.cycleType": 1, //天还是周
"cycleTime.dateBegin": "2021-12-15",//起始时间
"cycleTime.dateEnd": "2021-12-15",//结束时间
roomApplyTimeType: 1,
timeBegin: "8:00", //开始时间
timeEnd: "12:00", //结束时间
})

主要是各个字段的含义,根据我尝试大概意思就是上面注释这样.

然后返回各个教室信息,数据处理就看个人了.

本人能力不足,直接莽了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.end(function (err, res) {
// console.log(res);
var free_classrooms = [];
const $ = cheerio.load(res.text);
var location;
var building;
var campus;
/*
教西A 130

*/
$('tbody[id] tr').each(function (i,elem) {
location = $(this).find('td:nth-child(2)').text();
building = $(this).find('td:nth-child(3)').text();
campus = $(this).find('td:nth-child(4)').text();
free_classrooms[i] = {
'教室位置':location,
'教学楼':building,
'校区':campus,
}

});
free_classrooms.forEach(function(value,index) {
console.log(value);
});

可以看到处理结果

image-20211215102207083

继续开坑,之前遇到了一系列问题,把程序放在服务器上结果报错等等.

之前是登陆后获得cookie再去请求空教室,这样导致请求空教室就必须去登陆,所以需要进行分离.

首先登录获得cookie,这是一个函数.然后再拿cookie去获得空闲教室.这是另一个函数,名字分别为getnewcookie()与getfreeclassroom().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
function getcookie() {
var all_course_info = "";
superagent.get(login_url).end(function (err, res) {
superagent
.post(login_url)
.type("form")
.set({
"Content-Type": "application/x-www-form-urlencoded",
Host: "us.nwpu.edu.cn",
Origin: "http://us.nwpu.edu.cn",
Referer: "http://us.nwpu.edu.cn/eams/login.action",
})
.send({
username: uid,
password: pwd,
encodedPassword: "",
session_locale: "zh_CN",
})
.end(function (err, res) {
var cookieid = res.headers["set-cookie"][0].split(";")[0].split("=")[1];
var cookie = `semester.id=158; JSESSIONID=${cookieid}; GSESSIONID=${cookieid}`;
superagent
.get(get_ids_url)
.set({
Cookie: cookie,
})
.end(function (err, res) {
var ids = res.text;
ids = ids.match(/form,"ids","(\d*)"/);
// console.log(ids);
superagent
.post(info_url)
.type("form")
.send({
ignoreHead: 1,
"setting.kind": "std",
startWeek: 1,
"semester.id": 158,
ids: `${ids}`,
})
.set("Cookie", cookie)
.end(function (err, res) {
if (res.statusCode == 200) {
var courses = [];
var teachers = [];
var weeks = [];
const $ = cheerio.load(res.text);
$("tbody[id] tr td:nth-child(5)").each(function (i, elem) {
teachers[i] = $(this).text();
});

$("tbody[id] tr td:nth-child(9)").each(function (i, elem) {
weeks[i] = $(this).text().trim();
});
$("tbody[id] tr td:nth-child(4)").each(function (i, elem) {
courses[i] = $(this).text();
});

courses.forEach((value, index) => {
value +=
" 老师:" +
teachers[index] +
"上课时间:" +
weeks[index] +
"周";

all_course_info += value + "\n";
});
// console.log(all_course_info+`cookie是${cookie}`);
getfreeclassroom("133","8:30",cookie);
}

});
});
})
})
}

getfreeclassroom()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
function getfreeclassroom(building, timebegin,cookie) {
var date = new Date().toLocaleString().split(',')[0].split('/');
var cycleTimebegin = date[2]+"-"+date[1]+"-"+date[0];
//注意 我放服务器上的时候这个tolocalestring日期顺序不对 这个也卡了很久
//也可以通过其他方法生成日期
var allinfo = [];
var timeend = +timebegin.split(":")[0] + 2 + ":" + timebegin.split(":")[1];

superagent
.post(free_url)
.type("form")
.set({
Cookie: cookie,
"Content-Type": "application/x-www-form-urlencoded",
Host: "us.nwpu.edu.cn",
Origin: "http://us.nwpu.edu.cn",
Referer: "http://us.nwpu.edu.cn/eams/classroom/apply/free.action",
})
.send({
"classroom.type.id": "", //教室类型
"classroom.campus.id": "", //校区
"classroom.building.id": building, //教学楼id
seats: "", //容量限制
"classroom.name": "", //教室
"cycleTime.cycleCount": 1, //时间周期
"cycleTime.cycleType": 1, //天还是周
"cycleTime.dateBegin": cycleTimebegin, //起始时间
"cycleTime.dateEnd": cycleTimebegin, //结束时间
roomApplyTimeType: 1,
timeBegin: timebegin,
timeEnd: timeend,
pageNo: "1",
})
.end(function (err, res) {
var free_classrooms = [];
const $ = cheerio.load(res.text);
var location;
var building_;
var campus;
/*
教西A 130

*/
$("tbody[id] tr").each(function (i, elem) {
location = $(this).find("td:nth-child(2)").text();
building_ = $(this).find("td:nth-child(3)").text();
campus = $(this).find("td:nth-child(4)").text();
free_classrooms[i] = {
教室位置: location,
教学楼: building_,
校区: campus,
};
});

free_classrooms.forEach(function (value, index)
{
allinfo.push(value);
});
var free_classrooms_info = "";
allinfo.forEach((value, index) => {
free_classrooms_info += JSON.stringify(value).slice(1, -1) + "\n";
});
console.log(`空闲教室信息\n${timebegin}-${timeend}\n${free_classrooms_info}`);

});

或者其他方法生成日期

References

JS HTTP 请求库哪家强?Axios?Request?Superagent?

superagent - npm (npmjs.com)

[译] SuperAgent中文使用文档

cheerio

Chinese README · cheeriojs/cheerio Wiki (github.com)

-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道