UNNC 课表抓取思路

背景

突然接手诺丁腔,想把课表抓取这块的代码重写一下整合进自己的服务。但这后面的科学道理没有表面上看起来那么简单。而原因并不是学校的反爬取策略多高明,而在于这课表系统的规律性实在是太难以捉摸、莫名其妙……

这里记录一下这些天的研究成果和最终思路。(不涉及代码层面,只说服务原理。)

更新

2019 年 1 月 28 日

初版。

查询入口

课表官网地址

UNNC 的课表查询地址是:

1
http://timetablingunnc.nottingham.ac.uk:8017

注意这里的端口号 8017 每年都会变动。2019 年是 8017,2018 年是 8005

同时这个系统和马来西亚校区共用,马来的 2019 年是 8016,2018 年是 8006

获取今年端口号的办法是访问下面的链接看新闻:

https://www.nottingham.edu.cn/en/academicservices/timetabling/teachingtimetable.aspx

实际上这个只是学校提供的供「方便地」查询的界面(事实上也没有很方便)。使用的服务是 Scientia® Web Server for Course Planner™,真正的地址是:http://timetablingunnc.nottingham.ac.uk:8017/reporting/。这个系统在很多其他学校也都在使用。通过这个地址可以看到更多暴露出的查询入口,但就是比较难查询,不明白数据格式长什么样子。

大二、大三、大四查询方式

根据官网的页面显示,可以通过以下几种方式查询课表。

  • Rooms(即各间教室的课表)
  • Schools/Divisions(即各个学院/部门的课表)
  • Programmes(即各个学制的课表)
  • Modules(即各个课程的课表)
  • Staff(即各个职工的课表)

神奇之处在于并没有直接显示最重要的「通过学号查询课表」的入口,而这个链接入口每年都是学校发邮件之后学生私下奔走相告得知(?)。链接如下:

1
http://timetablingunnc.nottingham.ac.uk:8017/reporting/individual;Student+Sets;id;16522132?template=Student+Set+Individual&weeks=1-52&days=1-7&periods=1-32

这里的 URL 参数规则如下:

参数 说明
16522132 学号。
weeks 周。全年是 1-52。不同学年的春秋季周数都不一样,所以推荐使用全年查询。
days 星期。一周七天是 1-7。虽然周六周日几乎不可能有课,但是以防万一还是推荐全周查询。
periods 时间。全天早八点到晚二十四点是 1-32。每个单位跨度是半小时,比如 1-3 指的是八点到九点。虽然十八点后几乎不会有课,但是以防万一还是推荐全天查询。

总之,保留其他参数,替换掉学号即可。

大一查询方式

大一不能直接通过学号查到,而是分组上课,需要根据组号查询。

官方推荐的学生查询的方案依旧是通过这个新闻链接下载一个包含所有分组情况的 PDF 后自己找。

我们的方法是通过 PDF 获取到组号信息后,通过组号查询课表,查询链接如下:

1
http://timetablingunnc.nottingham.ac.uk:8017/reporting/Individual;Student+Sets;name;Year%201-B-01?template=Student+Set+Individual&weeks=1-52&days=1-7&periods=1-32

这里的 URL 参数和之前的类似,需要替换成想要查询的组号的是 Year%201-B-01 字段。

抓取思路

通过 Rooms/Schools/Programmes/... 任一方式查询课表,并选择「list view」调至列表查看,可以发现,这个课表系统把每一门课称为「Module」,每一门课的日程称为「Activity」。

比如,以下是 CELE 的课程表。

Activity Module Name of Type Size Day Start End Duration Room Room Description Room Size Staff Weeks
CELEN038/S1(wk 2-3)/13 - Year 1-B-32,33 The Scientific Method Seminar 35 Monday 9:00 10:00 1:00 PB-418(COMP)+ Portland Building-Computer Lab 418 - Level 4 35 Zheng Wang 4-5
CELEN038/S1(wk 2-3)/26 - Year 1-C-36,37 The Scientific Method Seminar 35 Monday 9:00 10:00 1:00 PB-119(COMP)+ Portland Building-Computer Lab 119 - Level 1 35 Brenden Theaker 4-5
CELEN036/S1/18 - Year 1-B-28 Foundation Algebra for Physical Sciences & Engineering Seminar 18 Monday 9:00 10:00 1:00 TB-221+ Teaching Building Seminar Room 221 - Level 2 18 Abdulrauf Shaikh 4-13

同样的「The Scientific Method」课程,会有不同的小组 Seminar,对应不同的 Activity。

我们提前爬取出所有的 Activity,并存储在数据库中。

而通过学号或小组查询得到的界面如下:

图中红框标出的都是 Activity 的名字。所以可以通过正则匹配出这些 Activity,再去数据库中逐一查询得到详细时间等信息。

不直接查询,而是如此拐弯操作的原因是更方便管理。

思路总结:

  1. 通过 Department 或 Module 查询,爬取出所有 Activity,并储存到数据库中。
  2. 通过学号/组号查询,匹配出 Activity 的名字,并在数据库中逐一查询。

可以考虑提前爬取所有学号/组号的课程并存在数据库中。但需要提前知晓学号区段,并做一定的容错处理——如果数据库中没有该学号的课程,可能是该学号没有统计在案,需要再次尝试去官网网页查询匹配。

注意

  • Activity 的名字并不惟一,会有重复。
  • 发现通过 Department 查询,总计可以查出 15095 个 Activity,即使去重后,也有 10800 个左右。而通过 Module 查询,只会有 5000 个左右,原因待究。

关联 Module

如果要把 Activity 关联一门课,只能通过 Module 查询的方式来获取数据。原因是其他的查询方式并没有直接给出一个 Activity 所属的 Module。

虽然 Activity 的名字中包含有 Module 信息,但模式过于复杂、不规律,很难处理。比如下面所列出的一些名字:

1
2
3
4
5
CELEN064/S1/05
CELEN051/S1 - Group A/03
CELEF006 & CELEF007/Presentation/04 - A-40-3 to 9
CELEF004/S1/07 - Year 1-A-19
CELEF002&F003/C1/09 - Year 1-C-06,07,10

Module 的名字可以在列表中找到,会规律得多。大概的模式如下:

1
2
3
AERO/1000/01
CELE/F005/01
CIVE/1014/01

但也有莫名不规律的,让人眼花缭乱,比如:

1
2
3
4
5
6
7
ABEE/1012 &1019 lecture
ABEE/2022&3025/01
ABEE/3025/01 (location/staff refer to ABEE/2022)
CELE/F006 & CELE/F007
CHEE/1023&1041&1042 Practical
CHEE/1023&ABEE2029&MMME1019/01 (Joint in wk 1-3)
CIVE/1018&2052 (wk1 lecture timetable refer to module individual timetable)

建议这些类型的需要人工提前单独识辨出,并编写特定代码为其「纠正」。

如果要在把 Activity 关联 Module 的同时,关联上其他查询方式,比如 Department、Program 等,可以通过其他方式查询后,根据 Activity 的星期、地点、开始时间、结束时间关联上。原理是:如果两个 Activity 的上课时间和地点一模一样,有理由认为它们是相同的 Activity。

日历生成

使用 ical-generator 可以生成日历提供导出能力。甚至可以提供订阅链接。

PDF 导出

目前的方案是使用 puppeteer 模拟浏览器访问后实时生成。但缺点是在服务器上占用内存会略多。可以考虑提前生成后存在服务器待用,但需要提前知晓学号区段,以及上述的容错处理。

另一种可能的方案是使用一些 PDF 生成器的库。待寻。

特殊情况

换课

如果学生调整课程,学校的系统不会给予更新。

系统应该将每个学生的课表抓取到后,存储在数据库中,并提供能力修改课表。

死活查询不到

可能是学校的系统本身有问题。检查该学生能否通过官网查询。

官网 Q&A

How do I view the web timetables?

Click here to view how the web timetables are generated

Why do we have clashes between teaching?

Clashes can occur for a number of reasons. If a course structure in Saturn has not been updated correctly, then the students will not be attached to the correct modules. If there are a large number of option modules available, then lack of teaching hours may restrict the number of clash free combinations. Where students have not chosen their options, those details will not be displayed in the timetable.

Why can't the timetables be the same every year?

Timetables are subject to the many yearly changes which affect teaching. These can include increases in student numbers, staff members leaving or arriving, constraints on staff availability, students choosing different options, course structure modifications, specific AV requirements, and differences in the rooms available to us.

What is the start and end time of a lecture/seminar etc?

All classes should start on the hour, and end ten minutes before the hour. So a class scheduled for 9.00 would start at 9.00 and end at 9.50. The early end is to allow time for the room to be cleared before the next class, and to comply with Health and Safety requirements in the areas around large lecture rooms.