北航健康上报打卡

1 引言

需求:打卡很容易忘记,想自动化处理此事。

声明:学习与技术讨论,勿实操!!!

2 思路

思路主要参考:https://github.com/mottled233/buaa_daily_report

  1. 在Windows下编写脚本,使用chrome+selenium,模拟浏览器点击;
    • 打开网页并登陆
    • 进行打卡
  2. 通过 win10toast 实现在 Win10 系统中发送桌面消息通知;
  3. 借助Server酱+PushDeer 推送打卡结果至手机;
  4. 最终,使用Windows自带的任务计划程序实现定时执行脚本。

3 实现

3.1 打卡脚本

daily_report.py

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import logging
from time import sleep
import time
import datetime
import requests
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By

user = "xxx" # 你的统一认证账号
passwd = "xxx" # 你的统一认证密码
position = ("39.97812", "116.343936") # 定位,经纬度, 这个经纬度是大运村三号楼
SCKEY = "xxx" # 微信推送api,到http://sc.ftqq.com/ 免费申请,不需要请留空
max_attempt = 5 # 失败重复五次


def log_config():
"""
日志文件配置
"""
log_file = "log.log"
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
logger = logging.getLogger("main")
fh = logging.FileHandler(log_file, mode='w')
fh.setFormatter(formatter)
ch = logging.StreamHandler()
ch.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
logger.setLevel(logging.INFO)
return logger


def sign_in():
"""
模拟签到
"""
logger.info("正在进行验证...")

# Step1 测试能否进入网页以及用户名密码是否正确
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
browser = webdriver.Chrome(chrome_options=chrome_options)

try:
url = "https://app.buaa.edu.cn/uc/wap/login?redirect=https%3A%2F%2Fapp.buaa.edu.cn%2Fsite%2FbuaaStudentNcov%2Findex"
browser.get(url)

# 账号密码
user_name_input = browser.find_element_by_css_selector(
'#app > div.content > div:nth-child(1) > input[type=text]')
user_name_input.send_keys(user)
user_pwd_input = browser.find_element_by_css_selector(
'#app > div.content > div:nth-child(2) > input[type=password]')
user_pwd_input.send_keys(passwd)
except:
logger.info("打开打卡网页失败,请确认网络")
send_message("北航打卡:打开打卡网页失败,请确认网络")
exit(0)

logger.info("成功打开打卡网页")

# 点击登录按钮
login_button = browser.find_element_by_css_selector('#app > div.btn')
ActionChains(browser).move_to_element(login_button).click(login_button).perform()
browser.implicitly_wait(2)

# 跳转并点击获取位置按钮
# 这样写是为了等待跳转页面加载出来
fail_cnt = 0
while True:
location_button = browser.find_elements_by_css_selector(
'body > div.buaaStudentNcov > div.buaaStudentNcov-bg > div > div:nth-child(5) > div > div.warp-list-choose > div.warp-list.two-warp-list.warp-list-margin > div.title-input.title-input-mergin-left')
if len(location_button) > 0:
logger.info("登录成功")
break
else:
# 出现密码错误提示框
if len(browser.find_elements_by_css_selector('div.wapat-btn-box')) > 0:
send_message("北航打卡:打卡失败,用户名密码错误,程序已退出,请检查")
logger.info("打卡失败,用户名密码错误,请检查")
exit(0)

# 若只是反应慢,重试
if fail_cnt >= max_attempt:
send_message("北航打卡:登录超时超过最大尝试次数,请检查网络或打卡系统已崩溃")
logger.info("登录超时超过最大尝试次数")
exit(0)
time.sleep(10)
browser.get("https://app.buaa.edu.cn/site/buaaStudentNcov/index")
logger.info("登录超时,正在重试")
fail_cnt += 1

# Step2 签到
browser.execute_script("window.navigator.geolocation.getCurrentPosition=function(success){" +
"var position = {\"coords\" : {\"latitude\": \"" + position[0] + "\",\"longitude\": \""
+ position[1] + "\"}};" +
"success(position);}")
time.sleep(5)

location_button = browser.find_element(By.XPATH, "/html/body/div[1]/div[1]/div/div[5]/div/div[2]/div[2]/div[2]/input")
ActionChains(browser).move_to_element(location_button).click(location_button).perform()
time.sleep(2)
location = location_button.get_attribute('value')
logger.info(f"成功输入经纬度,定位{location}")

browser.implicitly_wait(3)
while True:
try:
submit_button = browser.find_element(By.XPATH, '/html/body/div[1]/div[1]/div/div[6]')
if submit_button.text.find("您已提交过信息") != -1:
result = "打卡失败,您已经提交过"
break
elif submit_button.text.find("未到填报时间") != -1:
result = "未到填报时间"
break
submit_button.click()
result = '提交成功'
break
except Exception as e:
try:
reason = browser.find_element_by_css_selector('#wapat > div > div.wapat-title').text
result = f'打卡失败,原因:{reason}'
break
except:
time.sleep(1)

browser.save_screenshot('location_test.png')
logger.info(result)
datee = datetime.date.today()
send_message("北航打卡:" + f"{result} {datee}")
sleep(50)
browser.quit()
logger.info("流程结束")


def send_message(msg):
"""
发送信息到手机,将结果写入临时文件,便于Windows消息提醒
"""
if SCKEY == "":
return
payload = {'text': msg}
requests.get(f"https://sc.ftqq.com/{SCKEY}.send", params=payload)

f = open("result.txt", "w")
f.write(msg)
f.close()


if __name__ == "__main__":
logger = log_config()
sign_in()

关于位置经纬度获取

  1. 打开网页版高德地图:https://www.amap.com/

  2. 选择指定位置,右键,选择这是哪儿

  3. 选择更多里面的分享

  4. 复制链接,在新的标签页打开,URL会自动补全,其中包含经纬度信息:lng=116.343936&lat=39.97812

    • 表示东经116.343936度,北纬39.97812度;

    • 上面脚本中的position变量是由维度和经度组成的二元组

关于手机信息推送

  1. 上面脚本中的变量SCKEY是填Server酱的Key

    图片名称
  2. 为了手机通知明显,通道配置使用PushDeer,手机安装PushDeer,配置pushkey:

    图片名称

3.2 Windows消息提醒脚本

buaa_notice.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from winrt.windows.ui.notifications import ToastNotificationManager, ToastNotification
import winrt.windows.data.xml.dom as dom

notifier = ToastNotificationManager.create_toast_notifier(r'xxx') # python.exe路径

title = "北航打卡"
f = open("result.txt", "r")
desp = f.read()
f.close()
tString = """<toast duration='short'><audio src = 'ms-winsoundevent:Notification.Reminder' loop = 'false' silent = 'false'/><visual><binding template='ToastText02'><text id="1">""" + title + """</text><text id="2">""" + desp + """</text></binding></visual></toast>"""

xDoc = dom.XmlDocument()
xDoc.load_xml(tString)
notifier.show(ToastNotification(xDoc))


if __name__ == "__main__":
print("打卡成功!")

3.3 定时执行脚本

buaa.bat

1
2
python daily_report.py
python buaa_notice.py

如何配置,参考Dukou网站自动化签到

4 参考