一个适用于某西北 985 高校「中国工科的第一大学」的疫情身体状况自动填报程序思路分享及其使用方法。
0x00 前言
在新冠疫情的背景之下,想必很多学校都要求学生每日申报自己的健康状况。但是日子一天天过去总有忘记的时候——轻则全班公开处刑,重则全院公开处刑,尴尬至极。后来接入了企业微信,如果不填每天中午 11:30 又准时来催你填……每天还得去对着表格整几下,实在有些麻烦,遂考虑制作一个自动化填报的程序来减轻自己的工作量。
本项目 Github 仓库:
如果 Github 访问异常,请浏览 Gitee 镜像。
请注意 / 在正文开始之前
本文「0x02 本地先行调试」中的操作建立于你能够独立在计算机配置 Python 环境的前提之上。你也可以直接在云端调试,这样便不需要在自己的计算机上配置 Python 环境。
警告 / 请认真阅读本部分
- 本软件设计之本意为技术学习,请在遵循法律及学校各项规定的前提下使用本软件。
- 如您需要使用该软件,请确保您的身体状况良好,如实申报自身身体状况。
- 若您的身体状况出现异常,应立即停止使用本软件、关闭云函数自动触发功能,并及时于学校系统更改每日申报情况。
- 因使用该软件误报身体状况而引发的不良后果应由您自行承担。
- 本软件原理是提取上一次的填报结果来提交,如果您的所在地发生改变,请自行手动填报一次,理论上程序会自动跟进后续的填报并与之同步。如出现异常烦请反馈!
- 该软件并非万能,请时常检查填报结果!
另:由于程序开发之初使用了国家现行最新的行政区划代码,故地理位置数据 location.py 可能与学校系统有部分出入,这有可能导致申报异常。如您在运行时发现提示:
1 | 获取上一次填报的信息时出现错误!可能是本程序地理位置数据库中的数据有误。 |
烦请带上所要申报的地理位置进行反馈,多谢!
如发现这样的异常,程序会自动阻断申报过程,防止错误的数据被上传至学校。
0x01 代码实现
文中涉及的全局变量:
1 | url_Form = 'http://yqtb.nwpu.edu.cn/wx/ry/jrsb.jsp' # 获取表格并进行操作 |
1.1 整体流程分析
要实现信息的填报,可以发现全流程大体需要经过四个步骤:CAS 登录→传递 Cookie 到疫情填报系统→获取上一次填报的 Form→提交 Form。接下来将分步说明每个步骤的实现。
1.2 模拟 CAS 登录
首先登入填报系统的主页:https://yqtb.nwpu.edu.cn。发现在未登录时,其跳转至 https://uis.nwpu.edu.cn/cas/login?service=http%3A%2F%2Fyqtb.nwpu.edu.cn%2F%2Fsso%2Flogin.jsp%3FtargetUrl%3Dbase64aHR0cDovL3lxdGIubndwdS5lZHUuY24vL3d4L3hnL3l6LW1vYmlsZS9pbmRleC5qc3A%3D 这一页面,该页面用于填入用户名及密码,这些信息将被打包 POST 出去,由 UIS 接受后跳转到 yqtb 的 service。
为了更容易地获取登录状态,从 CAS 登录网址 url_for_id 无跳转地发起登录,直接获得登录状态,与 Form 的 submit 过程分开。
于是首先建立一个 session 并对登录页发起 get:
1 | session = requests.Session() |
通过 Chrome 抓包发现,在 uis.nwpu.edu.cn登录时,CAS 首先接到第一组 POST,同时前端生成了 SESSION 作为第一个 cookie:

提取以上信息,打包第一组 header 和 data,然后对 url_for_id 模拟 POST:
1 | header = { |
之后 url_for_id 会如图返回登录状态。如果登录成功成功则返回文字内容:欢迎使用 统一身份认证 系统。这里直接在网页中查找这个字段来判断登录是否成功:

2020 年 8 月 6 日翱翔门户进行了微调,返回登录状态页面只返回欢迎使用四个字。因此下方代码块第三行需要对应修改为:
1 | if rt.find('欢迎使用') != -1: |
1 | rt = session.post(url_for_id, data=data, headers=header, timeout=5).text |
至此我们已经成功登入学校 CAS 系统,接下来需要到 yqtb.nwpu.edu.cn 进行健康状况的申报。
1.3 传递登录信息到疫情填报页面
在来到疫情填报页面前,我们还需要观察 url_for_id 在登录成功后返回的 cookie:它携带了此前生成的第一个 cookie SESSION,且又生成了第二个 cookie TGC。

利用 session.cookies.values() 来得到已经获取的 cookie 列表,分别存入 header。
紧接着访问疫情填报页面 url_Form,发现又生成了第三个 cookie JSESSIONID。这里先将其储存到 header3,否则后续在 yqtb.nwpu.edu.cn 执行的步骤会因为缺少 cookie 传递而失败。

在代码中即是先伪造一次对 url_Form 的请求简单来说就是「我就蹭蹭不进去」,目的是为了「骗」到 JSESSIONID。
至此,全流程所需的三个 cookie 都已经获取成功,这样就可以为接下来的 POST 所用。
这时可以正式登入疫情填报页面。通过抓包发现,这里需要此前获得的 TGC 作为 ticket 来从 UIS 跳转到 yqtb,同时 header 需要携带 JSESSIONID。故构造 header3 和 data2 来 POST:
1 | header3 = { |
然后完成 yqtb 系统的登录:
1 | r_log_on_yqtb2 = session.post(url_for_yqtb_logon, data=data2, headers=header3) |
之后就可以获取其他的信息了。
1.4 获取 Form 信息
先手动在浏览器上完成一次健康申报过程,抓包观察 POST 出去的数据,然后在代码中打包。
1 | HeadersForm = { |
这里面我们会发现一些变量需要动态地从疫情填报网站中获取,例如 userName、szcsbm、szcsmc 等,接下来对这些数据的动态获取进行介绍。
1.4.1 个人填表信息的获取
这一部分主要使用 BeautifulSoup 解析前端代码中表明的相关信息,并用正则表达式来提取。
其中 r2 获取到姓名和学院;r3 获取到电话号码;r5 获取上次填报的所在地,同时由此进行对「在外地」、「在西安」、「在学校」三种情况的判断。
1 | r2 = r_log_on_yqtb2.text |
需要注意的是 loc_code 的获取,这个数据没有办法通过前端获取的 html 来解析,因此需要通过 location.GetLocation(loc_name) 来进行匹配。因为返回的是一个 list,所以还要把它转换为 str。
当人在西安市内时,即 loc_name != '在西安' or loc_name != '在学校',这个值留空。
1.5 提交 Form 信息
通过上述操作,我们已经得到了填表所需要动态获取的各个变量:loc_code_str、loc_name、RealName、RealCollege 和 PhoneNumber。接下来向 url_Submit post 整个 form,也就是 1.4 部分中所提到的 form。
篇幅所限这里就省略了,看上面 1.4 吧。
1 | HeadersForm = {...} |
POST 完以后,不出意外学校的服务器已经收到了我们填报的数据,接下来需要对结果进行分析。
1.6 结果反馈及 Email 发送
通过 r4 来 GET url_Form,确认是否成功上传数据:
1 | r4 = session.get(url=url_Form, data=data2, headers=header3).text |
Email 的实现直接看 sendEmail() 就好了,这部分就纯套用了,确实没啥值得说的了。233333
0x02 本地先行调试 / 基础用法
计算机需要配置 Python 3.6 或以上版本的环境。
- 首先通过
git clone或直接在 Github 上将整个程序打包下载下来,解压进行配置。 - 根据「0x03 程序配置」一节来配置程序。
程序包自身已经封装了所需的第三方库。
如果你还是需要在计算机上安装所需 Requirements,在 Terminal (CMD) 中cd到程序目录,通过pip安装依赖项:pip install -r requirements.txt
- 使用
Python或VSCode等软件,在自己的机器上测试几遍。
之后就可以进行相关的部署了。
0x03 程序配置
该程序需要配置西工大翱翔门户账号、邮箱信息(可选)才能正确运行。
使该程序正确运行,需要编辑 main.py 中的部分变量,配置西工大翱翔门户账号、邮箱信息(可选)。其他文件无需改动。
main.py 需要设置的变量如下:
| 变量 | 说明 |
|---|---|
username |
填入登录翱翔门户的用户名,通常为学工号 |
password |
填入对应用户的密码 |
email_switcher |
Email 服务开关,默认开启服务,赋值为 1;填 0 则关闭如果关闭了 Email 服务则不需要配置以下变量。 |
mail_host |
(可选)发送方的 SMTP 服务器,如果使用 163 邮箱则不必修改;其他邮箱请对应更改。 |
mail_SMTPPort |
(可选)发送方的 SMTP 端口号,如果使用 163 邮箱则不必修改;其他邮箱请对应更改。 |
mail_user |
(可选)发送方邮箱,格式为 ****@***.com。 |
mail_pass |
(可选)发送方邮箱对应的密码。 |
receivers |
(可选)接收方邮箱;以 列表 (list) 形式存入。如果留空,则默认发送至发件邮箱。 |
对于 receivers 参数,如果只有一个接收邮箱,按照以下形式填入:
1 | ['*******@***.com'] |
如果有多个接收邮箱,则按照以下形式填入:
1 | ['******1@***.com', '******2@***.com', '...'] |
其余文件不需要进行修改。
0x04 云端部署
这里以阿里云函数计算为例。
- 首先注册一个阿里云账号,然后在控制台中搜索并进入「函数计算」。

- 点击「立即创建函数」。
- 选择「事件函数」。
- 按照下图的设置配置函数。其中
所在服务和服务函数可以任填

- 出现弹窗,点击「同意授权」。
- 找到新建的函数,选择「代码执行」→「代码包上传」,将你已经配置好的程序打包为 zip,然后上传。生成的压缩包文件结构应如图。上传后点击「保存」。

- 点击「保存并执行」,进行一次测试,观察输出的结果

- 如果输出结果无问题,点击「触发器」→「创建触发器」,如图填入参数。
触发器名称可任填,时间配置则在「时间间隔」中填入适当的时间间隔即可,然后点击「确定」。

程序触发的时间点是:北京时间每日 0 时起的每个「时间间隔」后。
至此便可以实现确定时间间隔的每日自动健康填报。
目前阿里云函数存在 bug,即每次函数执行可能重复。所以你可能会在同一时间收到两次邮件,这我暂时不知道怎么解决,可能是阿里云的锅吧。应该不会是我的锅吧,不会吧不会吧
收到的 Email 效果如下:

如果需要关闭云端的自动填报,在「触发器」菜单中关闭即可。

关于潜在的收费提示
在建立函数过程中,你可能会发现关于该功能的收费提示。本函数的请求量、请求时间及耗费公网流量均极小,每天执行三四次水平的请求的花费可以说近似是 0(考虑到会产生公网流量,费用可能是 0 一直写到两位以上小数的水平),请放心。大不了别充钱嘛!
具体收费标准详见:https://help.aliyun.com/document_detail/54301.html
关于本地部署
你也可以通过 Windows 的「计划任务」或类似功能在本地计算机上定时执行该程序,方法不再赘述。设计初衷还是为了云函数考虑的,这样更方便一些,不需要本地计算机挂机运行。
0xFF 后记
这算是我个人第一次爬虫的尝试吧…… 也算是又一次感受到了 Python 的魅力。将整个过程记录下来也算是一种学习和巩固了。
至于代码本身以及这篇文章写得是相当混乱,可读性是不咋滴【滑稽】,很可能逻辑上也不怎么简洁高效。也希望各位看官多多包涵了,欢迎批评指正!
后期应该会增加对于 ServerChan 微信推送的支持吧,毕竟这个比 Email 好用多了。
关于 ServerChan 的接入已更新。请参阅文章『妈妈再也不用担心我忘记填报疫情(Ⅱ)——利用 Server 酱完成填报结果推送』。
最后,还是期望全人类包括美国能够早日战胜 COVID-19,这个程序早一天失去它的用武之地吧~