学习通自动打卡

本文最后更新于:2021年2月12日 下午

上学期开学前一段时间,学校就让我们在学习通上打卡,用来确定每个同学返校前的健康状况?这学期又是这样的操作,而且要求的更严格了,放寒假还天天被人催着打卡,就很无语。讲道理哪个同学有异常情况的话,当地相关部门会直接通报到学校的吧。所以我个人觉得这个打卡没什么意义,而且我天天宅在家里,应该也不会有什么异常情况,所以搞一个自动打卡应该没问题吧😂。

更正

昨天晚上测试的时候学生健康登记这个模块在首页是没有显示的,当时我以为不用打卡这个模块,只需要定位签到就行了,没想到今天它又回来了(佛了。

解决这个问题只需要将注释的代码取消注释就可以了(下面代码已改

直接用我这个代码的同学需要注意

  • 代码前面需要填写账号密码和地址(这个地址是用来定位打卡的
  • 学生健康登记打卡里面的formData中也有内容需要自己修改,我把需要改动的地方用‘📌’这个标签括起来了,这样比较显眼😂
  • formData内容比较多,建议复制代码到本地,然后在编辑器中打开自动换行再修改(不然比较伤眼

分析打卡流程

要自动打卡应该分为两个步骤

  1. 登录
  2. 打卡

平时打卡都是在学习通APP上进行的,所以直接在手机抓包分析。

登录流程

APP到登录页面,然后打开抓包,然后登录,分析抓取到的请求,在众多请求包中一眼看到这个域名是passport开头的,点进来一看,好家伙,设置了13个cookie,多半是这个api没错了,再确认一下,看看请求内容,带了账号密码,那就是这个包在做登录操作了。

image-20210211110139434

image-20210211105918543

测试看看,把这个api拿到浏览器上试试,验证通过,那没问题了。

image-20210211110359697

image-20210211110509172

同时在刚刚手机上抓到的包中找到了学习通首页的url,后面的操作就可以在电脑上完成了,体验+。

image-20210212013902597

再然后就是一步一步的模拟打卡过程(其实就用浏览器的f12里面的network功能就行了

整个打卡过程貌似有四次post请求。

从域名上看😂😂

  • 第一个是获取表单内容的(后面发现同时还设置了一个CheckCode来控制每天打卡一次
  • 第二个是用来计数的(并不知道计数什么,但是貌似不影响
  • 第三个是用来校验填写的内容是否合法的(不用校验也能进行第四步
  • 第四个是保存我们填写的信息的

image-20210212015254019

image-20210212014858781

image-20210212015405610

image-20210212015539797

经过一些测试,得出结论,我们只需要访问第一个api获取checkCode,然后用这个checkCode去第四个api保存我们的打卡信息就可以了。

代码

# -*- coding: utf8 -*-
import requests, urllib, re

address = '地址'
params_login = {
  'uname': '账号',
  'code': '密码',
  'loginType': '1',
  'roleSelect': 'true'
}
api_save = 'http://office.chaoxing.com/data/apps/forms/fore/user/save?lookuid=%s'
api_login = 'https://passport2-api.chaoxing.com/v11/loginregister?cx_xxt_passport=json'
api_apply = 'http://office.chaoxing.com/front/web/apps/forms/fore/apply?uid=83260789&code=2iVkZKN1&mappId=3820629&appId=92610a6cf5e6415c8b4c2ac9c6a9a0f1&appKey=DhXDEM4W331834H2&id=1651&enc=3e98bd89c8ce6bcabf08dbad8553f3c1&state=1432&formAppId=&fidEnc=49b6a032fb2a0f2b'

def login():
  resp = requests.post(api_login, params = params_login)
  if (resp.status_code == 403):
    raise Exception('403登录失败')
  return resp.cookies

def get_checkCode(resp):
    code = re.findall(r"checkCode.*'(.*)'", resp.text)
    if code:
        return code[0]
    else:
        raise Exception("checkCode解析失败")

# 学生健康登记
def checkin1(cookies):
  checkCode = get_checkCode(requests.get(api_apply, cookies = cookies))
  params_c1 = {
    'formId': '1651',
    'formAppId': '',
    'version': '2',
    'formData': '[{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"selectbox","otherAllowed":false,"layoutRatio":1,"latestValShow":true,"id":13,"optionsLoadFromUrl":{"isLoadFromUrl":false,"response":[],"url":[],"urlHeaders":[]},"fields":[{"visible":true,"editable":true,"values":[{"val":"📌这里需要填写你所在的学院,同时再后面内容找到你所在的院系,将前面的false改为true📌"}],"options":[{"idArr":[],"checked":false,"title":"钒钛学院"},{"idArr":[],"checked":false,"title":"经济与管理学院"},{"idArr":[],"checked":false,"title":"交通与汽车工程学院"},{"idArr":[],"checked":false,"title":"康养学院"},{"idArr":[],"checked":false,"title":"人文社科学院"},{"idArr":[],"checked":false,"title":"生物与化学工程学院"},{"idArr":[],"checked":false,"title":"数学与计算机学院"},{"idArr":[],"checked":false,"title":"土木与建筑工程学院"},{"idArr":[],"checked":false,"title":"外国语学院"},{"idArr":[],"checked":false,"title":"艺术学院"},{"idArr":[],"checked":false,"title":"医学院"},{"idArr":[],"checked":false,"title":"智能制造学院"},{"idArr":[],"checked":false,"title":"研究生处"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"二级学院","fieldType":{"type":"string"}}],"optionScoreShow":false,"optionScoreUsed":false,"optionBindInfo":{"bindFieldId":0,"bindFieldIdx":-1,"isBinded":false,"bindFormId":0,"originalOptions":[{"idArr":[],"checked":false,"title":"选项1"},{"idArr":[],"checked":false,"title":"选项2"},{"idArr":[],"checked":false,"title":"选项3"}]},"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"editinput","layoutRatio":1,"formula":{"selIndex":-1,"calculateFieldId":0,"status":false},"latestValShow":false,"id":1,"fields":[{"visible":true,"values":[{"val":"📌这里填写你的年级专业班级📌"}],"verify":{"unique":{"errMsg":"此项内容已存在,不允许重复提交","open":false},"format":{"type":""},"required":{}},"tip":{"imgs":[],"text":""},"label":"年级、专业、班级","sweepCode":false,"fieldType":{"type":"string"},"codeChangeable":false,"editable":true}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":2,"fields":[{"visible":true,"values":[{"val":"健康","isOther":false}],"options":[{"idArr":[],"checked":true,"title":"健康"},{"idArr":[11],"checked":false,"title":"有发烧/咳嗽等症状"},{"idArr":[],"checked":false,"title":"其他"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"健康状况","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"editinput","layoutRatio":1,"formula":{"selIndex":-1,"calculateFieldId":0,"status":false},"latestValShow":false,"id":3,"fields":[{"visible":true,"values":[{"val":"📌这里填写你的省市区📌"}],"verify":{"unique":{"errMsg":"此项内容已存在,不允许重复提交","open":false},"format":{"type":""},"required":{}},"tip":{"imgs":[],"text":""},"label":"当前所在省、市、县(区)","sweepCode":false,"fieldType":{"type":"string"},"codeChangeable":false,"editable":true}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":14,"fields":[{"visible":true,"values":[{"val":"否","isOther":false}],"options":[{"idArr":[11],"checked":false,"title":"是"},{"idArr":[],"checked":true,"title":"否"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"14天内是否曾前往疫情防控重点区域","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":4,"fields":[{"visible":true,"values":[{"val":"否","isOther":false}],"options":[{"idArr":[11],"checked":false,"title":"是"},{"idArr":[],"checked":true,"title":"否"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"14天内是否有与高危人员有密切接触历史","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":5,"fields":[{"visible":true,"values":[{"val":"否","isOther":false}],"options":[{"idArr":[11],"checked":false,"title":"是"},{"idArr":[],"checked":true,"title":"否"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"家中或亲人是否有感染者或疑似感染者","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":6,"fields":[{"visible":true,"values":[{"val":"否","isOther":false}],"options":[{"idArr":[11,7,8],"checked":false,"title":"是"},{"idArr":[],"checked":true,"title":"否"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"自己是否为疑似病例","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":7,"fields":[{"visible":true,"values":[],"options":[{"idArr":[11],"checked":false,"title":"是"},{"idArr":[],"checked":false,"title":"否"}],"verify":{},"tip":{"imgs":[],"text":""},"label":"是否被隔离","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":false,"hasAuthority":true},{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"editinput","layoutRatio":1,"formula":{"selIndex":-1,"calculateFieldId":0,"status":false},"latestValShow":false,"id":8,"fields":[{"visible":true,"values":[{"val":""}],"verify":{"unique":{"errMsg":"此项内容已存在,不允许重复提交","open":false},"format":{"type":""}},"tip":{"imgs":[],"text":""},"label":"隔离地点","sweepCode":false,"fieldType":{"type":"string"},"codeChangeable":false,"editable":true}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":false,"hasAuthority":true},{"compt":"radiobutton","otherAllowed":false,"comptCombined":true,"layoutRatio":1,"latestValShow":true,"id":9,"fields":[{"visible":true,"values":[{"val":"否","isOther":false}],"options":[{"idArr":[11,10],"checked":false,"title":"是"},{"idArr":[],"checked":true,"title":"否"}],"verify":{"required":{}},"tip":{"imgs":[],"text":""},"label":"是否已确诊病例","fieldType":{"type":"string"},"editable":true}],"optionScoreShow":false,"optionScoreUsed":false,"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true},{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"editinput","layoutRatio":1,"formula":{"selIndex":-1,"calculateFieldId":0,"status":false},"latestValShow":false,"id":10,"fields":[{"visible":true,"values":[{"val":""}],"verify":{"unique":{"errMsg":"此项内容已存在,不允许重复提交","open":false},"format":{"type":""}},"tip":{"imgs":[],"text":""},"label":"住院地点","sweepCode":false,"fieldType":{"type":"string"},"codeChangeable":false,"editable":true}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":false,"hasAuthority":true},{"linkInfo":{"linkFormType":"","condFields":[],"linkFormId":0,"linkFormValueFieldCompt":"","linkFormIdEnc":"","linkFormValueFieldId":0,"linked":false,"currFormCondFieldId":0},"compt":"edittextarea","layoutRatio":1,"latestValShow":false,"id":11,"fields":[{"visible":true,"values":[{"val":""}],"verify":{},"tip":{"imgs":[],"text":""},"label":"需要说明的情况","fieldType":{"type":"string"},"editable":true}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":false,"hasAuthority":true}]',
    'uniqueCondition': '[]',
    't': '1',
    'enc': '3e98bd89c8ce6bcabf08dbad8553f3c1',
    'checkCode': checkCode,
    'gatherId': '0',
    'gverify': ''
  }
  url = api_save % (cookies['_uid'])
  resp = requests.post(url, params = params_c1, cookies = cookies)
  if '每个用户每日填写1次,您的提交已达上限哦~' in resp.text or '"success":true' in resp.text :
    return '健康登记打卡成功或已打卡过'
  else:
    print(resp.text)
    return '健康登记打卡失败'

# 定位打卡
def checkin2(cookies):
  checkCode = get_checkCode(requests.get(api_apply, cookies = cookies))
  params_c2 = {
    'formId': '8273',
    'formAppId': '',
    'version': '1',
    'formData': '[{"compt":"location","id":1,"fields":[{"verify":{},"tip":{"imgs":[],"text":""},"label":"定位","fieldType":{"type":"point"},"editable":true,"visible":true,"values":[{"address":"%s","lng":104.083898,"lat":30.759099}]}],"inDetailGroupIndex":-1,"fromDetail":false,"isShow":true,"hasAuthority":true}]' % (address),
    'uniqueCondition': '[]',
    't': '1',
    'enc': '61d91ce3a502d4f32332ed15d8c42e33',
    'checkCode': checkCode,
    'gatherId': '0',
    'gverify': ''
  }
  url = api_save % (cookies['_uid'])
  resp = requests.post(url, params = params_c2, cookies = cookies)
  if '每个用户每日填写1次,您的提交已达上限哦~' in resp.text or '"success":true' in resp.text :
    return '定位打卡成功或已打卡过'
  else:
    print(resp.text)
    return '定位打卡失败'

# 云函数入口
def main_handler(event, context):
  cookies = login()
  return checkin1(cookies) + '\n' + checkin2(cookies)

if __name__ == '__main__':
  print(main_handler(None, None))

本地测试通过

image-20210212020408353

image-20210212020504083

image-20210212020551151

image-20210212020625136

部署到云函数或者github action

我这里选择部署到国内运营商的云函数,因为比较稳定(并不,github action也是可以的。

这里是阿里云函数服务的流程。

image-20210212021739139

image-20210212021756288

image-20210212021835137

翻车😭

image-20210212022307621

没找到原因,直接换了腾讯云函数碰碰运气,结果就可以了??? 为什么啊(腾讯云函数的操作和阿里的大差不差

image-20210212022447353

然后设置一个每天自动打卡的触发器

image-20210212022524763

大年初一凌晨2.26,我居然在搞这个玩意。。。

脑子有点迷迷糊糊的,写的比较混乱,很多地方之前没截图就跳过了😂,有不懂可以发评论😂(会有人看吗😐,同时欢迎大佬指点。

顶不住了,睡了睡了。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!