欧家欣

忠於自己 熱愛生活

0%

疫情通自动打卡

功能

python脚本+腾讯云函数,实现微信疫情通每日自动打卡。

打卡记录展示

我用腾讯云函数每天早上8点定时执行脚本,将打卡结果发送到了自己的邮箱,下面是邮箱中收到的2月2日-2月5日打卡记录,“操作成功”就是打卡成功了。

实现思路

正常打卡时,每天只需要填好表单后点击“提交信息”按钮就完成打卡了,本质上是向某个服务器发送了一个POST请求,将体温、位置等信息提交到服务器。所以只需要知道向哪个服务器提交了什么数据这两个信息,就可以用代码向相同的服务器发送相同的数据实现打卡。将打卡代码放置在云服务器上,让脚本每日定时执行,就能够实现每日自动打卡,可以使用腾讯云函数,非常方便,而且免费的流量也足够用了。

实现过程

  1. 首先要找到POST请求的地址

    • 打开微信疫情通打卡页面,点击右上角三个点

    • 复制链接

    • 用电脑Edge浏览器(其他浏览器获取不到地址)打开复制的链接,自动跳转到了登录页面,F12打开浏览器控制台,或者点击鼠标右键再点击检查,因为需要查看登录以及提交表单的数据请求和响应

    • 输入学号,密码登录完成后,把表单信息填写完整,点击“提交信息”按钮,控制台切换到network网络面板,点击XHR,可以看到有两条异步请求信息。其中的save就是向服务器提交体温信息的POST请求,打开之后可以看到POST请求的地址

  2. 接下来要弄清POST请求提交的数据。

  3. 表单怎样和学生对应?

    • 提交的表单信息中并没有学号等信息,那提交的信息怎样和每个学生对应起来呢?

      上图是POST请求参数,开始以为是用请求参数中这几个id来标识用户的,但多次测试提交失败之后发现这几个id不能标识用户。回想正常打卡流程,第一次打卡时需要登录,以后打卡就不需要登录了,显然用户登录的信息被保存了下来,以后的打卡只需要将保存的用户信息一并提交,服务器就知道是同一个人。

    • 上图是POST请求头信息,仔细观察,发现请求头中cookies有多个字段,经过多次提交测试,发现其中的两个字段,一个是“eai-sess”,另一个是“UUkey”是与鉴别用户有关的。这两字段在随表单数据一起被提交到了服务器后,服务器根据这两个参数就知道是哪个学生提交的信息。所以要先获取这两个参数。

  4. 获取“eai-sess”和“UUkey”参数

    • 这两参数应该是登录时创建的,登录验证就是名为check的POST请求,点击check请求查看请求数据,发现果然是登录成功后,服务器为浏览器设置了这两个cookie。所以只需要再提交一个POST请求模拟登录,解析响应头中Set-Cookie字段就能获取到这两个参数。

  5. 整个提交流程如下

       graph TD
          A["

    操作目的: 获取eai-sess和UUKey参数

    POST请求地址: https://app.xaut.edu.cn/uc/wap/login/check

    请求参数: {username: 学号, password: 密码}

    用正则表达式匹配响应头Set-Cookie字段中的eai-sess和UUKey

    "] --eai-sess 和 UUKey--> B[

    操作目的: 提交信息, 进行打卡

    POST请求地址: https://app.xaut.edu.cn/ncov/wap/default/save

    请求参数: 表单中的信息

    将提交结果通过邮件发送到手机

    ]
  6. 增加发送邮件功能

    将提交结果发送到指定的邮箱,当程序运行出现错误时,就能及时接收到通知。发送邮件可以用自己的邮箱给自己的邮箱发送,发送方邮箱要开通SMTP服务。如果不需要发送邮件将主函数中调用发送邮件的语句注释掉。

  7. 创建腾讯云函数,让脚本每日自动运行,步骤如下

    云函数官方简介“云函数(Serverless Cloud Function,SCF)是腾讯云为企业和开发者们提供的无服务器执行环境,帮助您在无需购买和管理服务器的情况下运行代码。您只需使用平台支持的语言编写核心代码并设置代码运行的条件,即可在腾讯云基础设施上弹性、安全地运行代码”,具体创建步骤看官方文档。这里需要使用定时触发器来触发代码执行,可以规定每天早上8点定时执行代码进行打卡。部署成功后如下

代码及注意

代码

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 文件名:yqt.py
# 作用:疫情通提交数据
import requests
import time
import re
import warnings
import smtplib
from email.mime.text import MIMEText
from email.header import Header


class user:
def __init__(self, username, password, province, city, county, nickname=""):
"""
username:学号
password:密码
province:省
city:市
county:县/区
nickname:昵称
"""
self.username = username
self.password = password
self.province = province
self.city = city
self.county = county
self.nickname = nickname


class yqt:
def __init__(self):
# 提交时间,默认获取当天时间
self.date = time.strftime("%Y%m%d")
# 体温默认第三档,36.5~36.9
self.tw = 3
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.56'}

def get_cookie(self, user):
"""模拟登录,获取cookie"""

# 登录入口
login_url = "https://app.xaut.edu.cn/uc/wap/login/check"
# 用户名和密码
data = {"username": user.username, "password": user.password}
# 发送POST请求,请求参数为用户名和密码
r = requests.post(login_url, data=data, headers=self.headers, verify=False)
# 解析RESPONSE的Set-Cookie域,获取eai-sess和UUkey两个参数
setcookies = r.headers.get("Set-Cookie")
ha = re.search(r"(?P<eai_sess>(?<=eai-sess=)\w+(?=;)).*(?P<UUkey>(?<=UUkey=)\w+(?=;))", setcookies)
cook = ha.groupdict()
# 返回cookie
cookie = {'eai-sess': cook['eai_sess'], 'UUkey': cook['UUkey']}
return cookie

def submit(self, user):
"""使用cookie,模拟特定用户提交数据"""

# 提交数据的接口
url = 'https://app.xaut.edu.cn/ncov/wap/default/save'
# 先模拟登录,得到cookie,即{eai-sess:xxx, UUkey:xxx}
cookie = self.get_cookie(user)
# 需要提交的数据
data = {
'area': user.province + user.city + user.county,
'province': user.province,
'city': user.city,
'date': self.date,
'tw': self.tw
}
# 发送POST请求
r = requests.post(url, cookies=cookie, data=data, headers=self.headers, verify=False)
# 返回响应结果
return r.text

def send_mail(self, mail_host, mail_user, mail_pass, receiver, subject, content):
"""
作用:发送邮件
mail_host: 邮件发送方服务器,例如"smtp.163.com"
mail_user: 邮件发送方邮箱,例如"xxx@163.com"
mail_pass: 邮箱发送方邮箱密码
receiver: 邮件接收方邮箱
subject: 邮件主题
content:邮件正文
"""
receivers = [receiver]
message = MIMEText(content, 'plain', 'utf-8')
message['From'] = mail_user
message['To'] = receiver
subject = subject
message['Subject'] = Header(subject, 'utf-8')
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)
smtpObj.set_debuglevel(1)
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(mail_user, receivers, message.as_string())
smtpObj.quit()

def send_detail_mail(self, content):
self.send_mail(mail_host="smtp.qq.com", mail_user="邮件发送方邮箱,例如'xxx@qq.com", \
mail_pass="邮件发送方密钥", receiver="邮件接收方邮箱,例如'xxx@qq.com", \
subject=time.strftime("%Y{y}%m{m}%d{d}").format(y='年', m='月', d='日') + "-疫情通提交结果", \
content=content)

# def main_handler(event, context):
if __name__ == "__main__":
# 忽略所有警告信息
warnings.filterwarnings('ignore')

# 创建用户列表
userList = []
userList.append(user(username="A同学学号", password="A同学研究生院登录密码", province="填所在省", city="填所在市", county="填所在县\区", nickname="A同学昵称,随意给"))
# 为多人打卡,只需添加用户信息即可
# userList.append(user(username="B同学学号", password="B同学研究生院登录密码", province="陕西省", city="西安市", county="碑林区", nickname="B同学昵称,随意给"))

# 创建打卡对象
yqt = yqt()

content = ""
for user in userList:
# 打卡提交
content += user.nickname
content += ': '
content += yqt.submit(user)
content += '\n'
time.sleep(3)
# yqt.send_detail_mail(content)

注意

  • 脚本运行环境为python3.6
  • 将主函数中用户列表中的信息替换为自己的信息(学号,密码,省,市,县/区,昵称)
  • 若需为多人打卡,在用户列表中append多个用户即可,云函数分配的运行内存配额要足够,否则会抛出异常
  • 若需要将执行结果发送到邮箱,需将主函数中yqt.send_detail_mail(content)去掉注释,并将此函数内部的邮箱地址改为自己的,且发送方邮箱必须开通SMTP服务,邮件才能发送成功。
  • 要将此代码部署到腾讯云函数,需要将主函数名替换为“def main_handler(event, context)”,细节参考云函数创建流程官方文档

本篇是学习记录,难免有误,欢迎指正。有任何问题可以在Issues里提问。