如何优雅地在北邮打卡、出校

由于疫情原因,每天都需要准时打卡上报数据,且出校需要申请。

尽管本质上该政策没有任何问题,但是对于大部分身体处于健康状态的人来说,每天要做的只是重复性地填写上一天已经填写过的东西。特别是请假出校,甚至连辅导员、手机号都需要每次重新填写。

尽管填写这些东西并不需要花费特别多的时间,但是仍然需要在各种连贯性的工作时,去继续执行这件事。对于这些重复性的工作,应该交由代码来解决。只有某天自己真的发烧时,再关闭脚本修改参数。

尽管目前已有相应的打卡脚本,但是不少都使用的 selenium。从结果上看,这些代码可用,但是从过程上看,这些代码极其不优雅。首先是 selenium 本身是模拟浏览器操作,会有额外的性能损耗。其次是 selenium 需要安装浏览器及其对应版本的浏览器驱动。本身一个非常简单的接口功能却需要安装很多实际上用不到的东西,非常浪费资源。

因此这里采用抓包找接口的形式来实现

打卡

打卡登录请求

首先在电脑端的微信打开疫情防控通填报页面,得到链接https://app.bupt.edu.cn/ncov/wap/default/index
这时如果为登录,会跳转到https://app.bupt.edu.cn/backend/default/login?redirect=%2F请求登录,测试可知这里账号是学号,密码为服务平台密码(默认为身份证后 8 位)

抓包可知,登录为一个 POST 请求,参数为账户和密码

地址 https://app.bupt.edu.cn/uc/wap/login/check 类型 POST 参数
参数名 类型 解释
username 字符串 账号(学号)
password 字符串 密码(默认身份证后 8 位)

返回值为 JSON 格式,其中e字段为状态码,如果为0说明没有问题,否则则需要查看m字段的错误信息

打卡请求

登陆成功后,即可进入打卡页面。由于一天只能打卡一次,因此要测试需要从页面本身着手。

查看页面的源码,可以发现该页面使用 Vue 实现,且未经过混淆,因此可以直接查看按钮逻辑。可以看到打卡按钮调用 Vue 的confirm()方法,并通过添加wapcf-btn-qx来实现禁止点击样式

<div class="footers">
    <a @click="confirm()" :class="{'wapcf-btn-qx': hasFlag == 1}" >提交信息(Submit)</a>
</div>

confirm()方法根据 Vue 的成员变量hasFlag来判断状态,如果打卡成功,则其为1。因此,如果想要调试,需要想办法把它重置为0

confirm() {
    if(this.hasFlag == 1) {
        wapalert('每天只能填报一次,你已提交过(Daily Report can only be submitted once a day. You have already submitted)','确认(Confirm)');
        return false;
    }
    if(!this.valid(this.info)) {
        return false;
    }
    wapconfirm('每天只能填报一次,请确认信息是否全部正确?(Daily Report can only be submitted once a day. Please confirm that all the information is correct.)', '确认<br>Submit', '再看看<br>Check again', function () {
        vm.save();
    });
},

尽管该页面关闭了调试模式,无法直接使用 Vue Devtools 插件来编辑,但是从源码可以看出,Vue 对象实例被赋值给了vm变量

var vm = new Vue({
    el: '.form-detail2',
    ...
});

那么,在控制台直接设置vm._data.hasFlag=0,即可重置其成员变量。如图,可以看到按钮已经可以点击

重置按钮状态重置按钮状态

接下来就可以抓包了。

打卡请求本身没有额外的验证,但是需要提交的参数非常多,不过基本都可以在页面本身就有的内容获取。

在代码中,有两部分重要内容defoldInfo,前者为全局变量,后者为 Vue 的成员变量。不过在爬虫里直接使用正则表达式提取即可。

表单数据来源表单数据来源

这两个变量中,def是当前表单生成的新数据(如当前的表单提交 ID),oldInfo则是上一次提交的值(前一天的数据)。只需要结合这两者的内容,既可以作为当日的新提交。

经过对比,可以得知需要使用新表单的数据只有datecreatedid三项,其余的都可以从oldInfo获取(如果不存在则留空(使用oldInfo.get("date","")可以在检索字典时,获取默认值))

涉及的字段如下:

oldKeys = [
    "ismoved",    "jhfjrq", "jhfjjtgj", "jhfjhbcc", "sfxk", "xkqq", "szgj", "szcs", "zgfxdq", "mjry", "csmjry", "uid",  "tw", "sfcxtz", "sfyyjc", "jcjgqr", "jcjg", "sfjcbh", "sfcxzysx", "qksm", "remark", "address", "area", "province", "city", "geo_api_info",  "sfzx", "sfjcwhry", "sfcyglq", "gllx",
    "glksrq", "jcbhlx", "jcbhrq", "sftjwh", "sftjhb", "fxyy", "bztcyy", "fjsj", "sfjchbry", "sfjcqz", "jcqzrq", "jcwhryfs", "jchbryfs", "xjzd", "sfsfbh", "jhfjsftjwh", "jhfjsftjhb", "szsqsfybl", "sfygtjzzfj", "gtjzzfjsj", "sfsqhzjkk", "sqhzjkkys", "created_uid", "gwszdd", "sfyqjzgc", "jrsfqzys", "jrsfqzfy"
]
defaultKeys = [
    "date", "created", "id",
]

将拼接好的数据通过 POST 方式发送至https://app.bupt.edu.cn/ncov/wap/default/save即可

同登录接口,这里返回的也是一个 JSON 数据,只需要判断e是否为0即可

打卡开源

代码开源与 Github,使用 Python 实现

OhYee/bupt-scripts/check

请假

请假登录

请假与打卡使用的是同一个账号系统,但是登录跳转的页面不同。

登录会跳转至https://auth.bupt.edu.cn/authserver/login

需要特别注意的是,这个页面为了防止 CSRF 每次都会生成一个不同的 LT 值,因此需要先使用 GET 请求获取该值(可以使用正则表达式<input type="hidden" name="lt" value="(.*)" />获取),再请求对应的登录接口

地址 https://auth.bupt.edu.cn/authserver/login?service=https%3A%2F%2Fme.bupt.edu.cn%2Fsite%2Flogin%2Fcas-login 类型 POST 参数
参数名 类型 解释
username 字符串 账号(学号)
password 字符串 密码(默认身份证后 8 位)
lt 字符串 需要从页面中获取
execution 字符串 固定为e1s1
_eventId 字符串 固定为submit
rmShown 整数 固定为1

请假请求

登录成功后,即可在https://service.bupt.edu.cn/v2/matter/start?id=578页面进行申请

在请求之前,可以访问https://service.bupt.edu.cn/site/user/get-name获取用户信息,一方面可以获取用户的学院信息,另一方面也是检查登录是否成功

地址 https://service.bupt.edu.cn/site/user/get-name 类型 GET