今日校园

讨厌签到?动个歪脑筋吧😂

前言

​ 总的来说,我已经是第二次疫情在家了,本人的记性一直都不太好,去年寒假的时候,每天还有一位热心的姐姐每天打电话问候我,我到现在都十分感谢那位给我打了一个寒假+一个学期电话的姐姐,但是现在,由于姐姐不能再继续提醒我了,但是这个记性依然还是很烂呀,而且我们学校每天需要填写的信息都是一样的,没办法,只能在网上搜索了一下教程,终于找到了一个与之相关的GitHub项目——auto-submit

从上传的时间来看,这个项目已经有了大半年了,而且有400 star,毫不犹豫选择这个,这篇文章将会分为两个目的来给大家介绍,一个是小白上手,另一部分则是分享我整个的经历

小白上手

​ 首先注册搜索并注册腾讯云

然后进入云函数

进入函数服务并且新建建云函数,自定义并且选择python 3.6

鼠标继续往下滑,将这里执行超时改成60s

完成即可,如法炮制,新建一个层,随便输入一个名称,提交方法为默认(本地上传zip)并且将dependency (点击即可下载),添加 python3.6 的运行环境

找到刚才的函数服务,进入刚刚创建的创建函数,清空其中的代码并且将下面的代码复制到其中并且保存

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# -*- coding: utf-8 -*-
import sys
import requests
import json
import yaml
import oss2
from urllib.parse import urlparse
from datetime import datetime, timedelta, timezone
from urllib3.exceptions import InsecureRequestWarning
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr

# debug模式
debug = False
if debug:
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


# 读取yml配置
def getYmlConfig(yaml_file='config.yml'):
file = open(yaml_file, 'r', encoding="utf-8")
file_data = file.read()
file.close()
config = yaml.load(file_data, Loader=yaml.FullLoader)
return dict(config)


# 全局配置
config = getYmlConfig(yaml_file='config.yml')


# 获取今日校园api
def getCpdailyApis(user):
apis = {}
user = user['user']
schools = requests.get(
url='https://static.campushoy.com/apicache/tenantListSort', verify=not debug).json()['data']
flag = True
for item in schools:
new=item['datas']
for one in new:
#print(one)
if one['name'] == user['school']:
flag = False
params = {
'ids': one['id']
}
res = requests.get(url='https://mobile.campushoy.com/v6/config/guest/tenant/info', params=params,
verify=not debug)
data = res.json()['data'][0]
joinType = data['joinType']
idsUrl = data['idsUrl']
ampUrl = data['ampUrl']
if 'campusphere' in ampUrl or 'cpdaily' in ampUrl:
parse = urlparse(ampUrl)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host

ampUrl2 = data['ampUrl2']
if 'campusphere' in ampUrl2 or 'cpdaily' in ampUrl2:
parse = urlparse(ampUrl2)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host
break
if flag:
log(user['school'] + ' 未找到该院校信息,请检查是否是学校全称错误')
exit(-1)
log(apis)
return apis


# 获取当前utc时间,并格式化为北京时间
def getTimeStr():
utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
return bj_dt.strftime("%Y-%m-%d %H:%M:%S")


# 输出调试信息,并及时刷新缓冲区
def log(content):
print(getTimeStr() + ' ' + str(content))
sys.stdout.flush()


# 登陆并返回session
def getSession(user, loginUrl):
user = user['user']
params = {
'login_url': loginUrl,
# 保证学工号和密码正确下面两项就不需要配置
'needcaptcha_url': '',
'captcha_url': '',
'username': user['username'],
'password': user['password']
}

cookies = {}
# 借助上一个项目开放出来的登陆API,模拟登陆
res = requests.post(config['login']['api'], params, verify=not debug)
cookieStr = str(res.json()['cookies'])
log(cookieStr)
if cookieStr == 'None':
log(res.json())
return None

# 解析cookie
for line in cookieStr.split(';'):
name, value = line.strip().split('=', 1)
cookies[name] = value
session = requests.session()
session.cookies = requests.utils.cookiejar_from_dict(cookies)
return session


# 查询表单
def queryForm(session, apis):
host = apis['host']
headers = {
'Accept': 'application/json, text/plain, */*',
'User-Agent': 'Mozilla/5.0 (Linux; Android 4.4.4; OPPO R11 Plus Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 yiban/8.1.11 cpdaily/8.1.11 wisedu/8.1.11',
'content-type': 'application/json',
'Accept-Encoding': 'gzip,deflate',
'Accept-Language': 'zh-CN,en-US;q=0.8',
'Content-Type': 'application/json;charset=UTF-8'
}
queryCollectWidUrl = 'https://{host}/wec-counselor-collector-apps/stu/collector/queryCollectorProcessingList'.format(
host=host)
params = {
'pageSize': 6,
'pageNumber': 1
}
res = session.post(queryCollectWidUrl, headers=headers,
data=json.dumps(params), verify=not debug)
if len(res.json()['datas']['rows']) < 1:
return None

collectWid = res.json()['datas']['rows'][0]['wid']
formWid = res.json()['datas']['rows'][0]['formWid']

detailCollector = 'https://{host}/wec-counselor-collector-apps/stu/collector/detailCollector'.format(
host=host)
res = session.post(url=detailCollector, headers=headers,
data=json.dumps({"collectorWid": collectWid}), verify=not debug)
schoolTaskWid = res.json()['datas']['collector']['schoolTaskWid']

getFormFields = 'https://{host}/wec-counselor-collector-apps/stu/collector/getFormFields'.format(
host=host)
res = session.post(url=getFormFields, headers=headers, data=json.dumps(
{"pageSize": 100, "pageNumber": 1, "formWid": formWid, "collectorWid": collectWid}), verify=not debug)

form = res.json()['datas']['rows']
return {'collectWid': collectWid, 'formWid': formWid, 'schoolTaskWid': schoolTaskWid, 'form': form}


# 填写form
def fillForm(session, form, host):
sort = 1
for formItem in form[:]:
# 只处理必填项
if formItem['isRequired'] == 1:
default = config['cpdaily']['defaults'][sort - 1]['default']
if formItem['title'] != default['title']:
log('第%d个默认配置不正确,请检查' % sort)
exit(-1)
# 文本直接赋值
if formItem['fieldType'] == 1 or formItem['fieldType'] == 5:
formItem['value'] = default['value']
# 单选框需要删掉多余的选项
if formItem['fieldType'] == 2:
# 填充默认值
formItem['value'] = default['value']
fieldItems = formItem['fieldItems']
for i in range(0, len(fieldItems))[::-1]:
if fieldItems[i]['content'] != default['value']:
del fieldItems[i]
# 多选需要分割默认选项值,并且删掉无用的其他选项
if formItem['fieldType'] == 3:
fieldItems = formItem['fieldItems']
defaultValues = default['value'].split(',')
for i in range(0, len(fieldItems))[::-1]:
flag = True
for j in range(0, len(defaultValues))[::-1]:
if fieldItems[i]['content'] == defaultValues[j]:
# 填充默认值
formItem['value'] += defaultValues[j] + ' '
flag = False
if flag:
del fieldItems[i]
# 图片需要上传到阿里云oss
if formItem['fieldType'] == 4:
fileName = uploadPicture(session, default['value'], host)
formItem['value'] = getPictureUrl(session, fileName, host)
log('必填问题%d:' % sort + formItem['title'])
log('答案%d:' % sort + formItem['value'])
sort += 1
else:
form.remove(formItem)
# print(form)
return form


# 上传图片到阿里云oss
def uploadPicture(session, image, host):
url = 'https://{host}/wec-counselor-collector-apps/stu/collector/getStsAccess'.format(
host=host)
res = session.post(url=url, headers={
'content-type': 'application/json'}, data=json.dumps({}), verify=not debug)
datas = res.json().get('datas')
fileName = datas.get('fileName')
accessKeyId = datas.get('accessKeyId')
accessSecret = datas.get('accessKeySecret')
securityToken = datas.get('securityToken')
endPoint = datas.get('endPoint')
bucket = datas.get('bucket')
bucket = oss2.Bucket(oss2.Auth(access_key_id=accessKeyId,
access_key_secret=accessSecret), endPoint, bucket)
with open(image, "rb") as f:
data = f.read()
bucket.put_object(key=fileName, headers={
'x-oss-security-token': securityToken}, data=data)
res = bucket.sign_url('PUT', fileName, 60)
# log(res)
return fileName


# 获取图片上传位置
def getPictureUrl(session, fileName, host):
url = 'https://{host}/wec-counselor-collector-apps/stu/collector/previewAttachment'.format(
host=host)
data = {
'ossKey': fileName
}
res = session.post(url=url, headers={
'content-type': 'application/json'}, data=json.dumps(data), verify=not debug)
photoUrl = res.json().get('datas')
return photoUrl


# 提交表单
def submitForm(formWid, address, collectWid, schoolTaskWid, form, session, host):
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 4.4.4; OPPO R11 Plus Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 okhttp/3.12.4',
'CpdailyStandAlone': '0',
'extension': '1',
'Cpdaily-Extension': 'Ew9uONYq03Siz+VLCzZ4RiWRaXXBubIGc1d7ecaS2YmSDf1+elDL0gdwAw977HbPzvgR3pkeyW3djmnPOMxYro3Tps7PNmLoqfNTAECZqcM1LAyx+2zTfDExNa4yDWs83AyTnSKXs7oHQvFOfXhKNY1OXVzIdnwOkgaNw7XxzM1+2efCWAJgUBoHNV3n3MayLqOwPvSCvBke+SHC/Hy/53+ehU9A1lst6JlpGiFhlEOUybo5s5/o+b/XLUexuEE50IQgdPL4Hi4vPe4yVzA8QLpIMKSFIaRm',
'Content-Type': 'application/json; charset=utf-8',
# 请注意这个应该和配置文件中的host保持一致
'Host': host,
'Connection': 'Keep-Alive',
'Accept-Encoding': 'gzip'
}

# 默认正常的提交参数json
params = {"formWid": formWid, "address": address, "collectWid": collectWid, "schoolTaskWid": schoolTaskWid,
"form": form,"uaIsCpadaily": True,"appVersion": "8.2.14"}
# print(params)
submitForm = 'https://{host}/wec-counselor-collector-apps/stu/collector/submitForm'.format(
host=host)
r = session.post(url=submitForm, headers=headers,
data=json.dumps(params), verify=not debug)
msg = r.json()['message']
return msg

title_text = '今日校园疫结果通知'

# 发送邮件通知
def sendMessage(send, msg):
if send != '':
log('正在发送邮件通知。。。')
res = requests.post(url='http://www.zimo.wiki:8080/mail-sender/sendMail',
data={'title': title_text, 'content': getTimeStr() + str(msg), 'to': send})

code = res.json()['code']
if code == 0:
log('发送邮件通知成功。。。')
else:
log('发送邮件通知失败。。。')
log(res.json())

def sendEmail(send,msg):
my_sender= config['Info']['Email']['account'] # 发件人邮箱账号
my_pass = config['Info']['Email']['password'] # 发件人邮箱密码
my_user = send # 收件人邮箱账号,我这边发送给自己
try:
msg=MIMEText(getTimeStr() + str(msg),'plain','utf-8')
msg['From']=formataddr(["FromRunoob",my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['To']=formataddr(["FK",my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['Subject']=title_text # 邮件的主题,也可以说是标题

server=smtplib.SMTP_SSL(config['Info']['Email']['server'], config['Info']['Email']['port']) # 发件人邮箱中的SMTP服务器,端口是25
server.login(my_sender, my_pass) # 括号中对应的是发件人邮箱账号、邮箱密码
server.sendmail(my_sender,[my_user,],msg.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
server.quit() # 关闭连接
except Exception: # 如果 try 中的语句没有执行,则会执行下面的 ret=False
log("邮件发送失败")
else: print("邮件发送成功")

# server酱通知
def sendServerChan(msg):
log('正在发送Server酱。。。')
res = requests.post(url='https://sc.ftqq.com/{0}.send'.format(config['Info']['ServerChan']),
data={'text': title_text, 'desp': getTimeStr() + "\n" + str(msg)})
code = res.json()['errmsg']
if code == 'success':
log('发送Server酱通知成功。。。')
else:
log('发送Server酱通知失败。。。')
log('Server酱返回结果'+code)

# Qmsg酱通知
def sendQmsgChan(msg):
log('正在发送Qmsg酱。。。')
res = requests.post(url='https://qmsg.zendee.cn:443/send/{0}'.format(config['Info']['Qsmg']),
data={'msg': title_text + '\n时间:' + getTimeStr() + "\n 返回结果:" + str(msg)})
code = res.json()['success']
if code:
log('发送Qmsg酱通知成功。。。')
else:
log('发送Qmsg酱通知失败。。。')
log('Qmsg酱返回结果'+code)

# 综合提交
def InfoSubmit(msg, send=None):
if(None != send):
if(config['Info']['Email']['enable']): sendEmail(send,msg)
else: sendMessage(send, msg)
if(config['Info']['ServerChan']): sendServerChan(msg)
if(config['Info']['Qsmg']): sendQmsgChan(msg)


def main_handler(event, context):
try:
for user in config['users']:
log('当前用户:' + str(user['user']['username']))
apis = getCpdailyApis(user)
log('脚本开始执行。。。')
log('开始模拟登陆。。。')
session = getSession(user, apis['login-url'])
if session != None:
log('模拟登陆成功。。。')
log('正在查询最新待填写问卷。。。')
params = queryForm(session, apis)
if str(params) == 'None':
log('获取最新待填写问卷失败,可能是辅导员还没有发布。。。')
InfoSubmit('没有新问卷')
exit(-1)
log('查询最新待填写问卷成功。。。')
log('正在自动填写问卷。。。')
form = fillForm(session, params['form'], apis['host'])
log('填写问卷成功。。。')
log('正在自动提交。。。')
msg = submitForm(params['formWid'], user['user']['address'], params['collectWid'],
params['schoolTaskWid'], form, session, apis['host'])
if msg == 'SUCCESS':
log('自动提交成功!')
InfoSubmit('自动提交成功!', user['user']['email'])
elif msg == '该收集已填写无需再次填写':
log('今日已提交!')
# InfoSubmit('今日已提交!', user['user']['email'])
InfoSubmit('今日已提交!')
else:
log('自动提交失败。。。')
log('错误是' + msg)
InfoSubmit('自动提交失败!错误是' + msg, user['user']['email'])
exit(-1)
else:
log('模拟登陆失败。。。')
log('原因可能是学号或密码错误,请检查配置后,重启脚本。。。')
exit(-1)
except Exception as e:
InfoSubmit("出现问题了!"+str(e))
raise e
else:
return 'success'


# 配合Windows计划任务等使用
if __name__ == '__main__':
print(main_handler({}, {}))
# for user in config['users']:
# log(getCpdailyApis(user))

然后在这个src目录新建一个文件,并且将它命名为config.yaml,这里写错了哈,图片上也错了(表现出我对yaml文件不熟悉😜)应该新建config.yml

将下面的yaml代码复制粘贴进去,并且将学号改为你自己的学号,密码改为你用学号登录的密码,地址改为你家的地址,如果你想需要server酱微信提醒,请点击server 酱,绑定微信和GitHub

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
#登陆相关配置
login:
#开放的模拟登陆api服务器地址
api: "http://www.zimo.wiki:8080/wisedu-unified-login-api-v1.0/api/login"
#用户组配置
users:
#单个用户配置
- user:
#username 学号或者工号
username: ''
#password 密码
password: ''
#address 地址,定位信息
address: #
#email 接受通知消息的邮箱
email: #
#school 学校全称
school: 燕山大学
cpdaily:
#表单组默认选项配置
defaults:
#表单默认选项配置,按顺序,注意,只有标必填项的才处理
- default:
#表单项类型,对应今日校园接口返回的fieldType字段,1代表文本,2代表单选,3代表多选,4代表图片
type: 1
#表单项标题
title: 晨检体温℃
#表单项默认值
value: 36.5
- default:
type: 1
title: 午检体温℃
value: 36.5
- default:
type: 2
title: 本人身体状况
value: 健康
- default:
type: 2
title: 家庭成员身体状况
value: 健康
- default:
type: 2
title: (是/否)有城际移动
value:
- default:
type: 2
title: (是/否)去医院就诊
value:
- default:
type: 2
title: 近十四天(是/否)接触过来自疫情高风险、中风险地区、境外或者去过疫情高风险、中风险地区、境外的亲友
value:
- default:
type: 2
title: (是/否)有新型冠状病毒感染的肺炎确诊或疑似病例接触史
value:
- default:
type: 2
title: 是否被集中隔离
value:
- default:
type: 2
title: 寒假离校返家后是否已经做过核酸检测
value:
- default:
type: 2
title: 核酸检测结果如何
value: 阴性
- default:
type: 2
title: 未完成核酸检测的原因
value: 已完成核酸检测

Info:
ServerChan: #
# 填写Server酱的SCKEY
Qsmg: # 填写Qsmg酱的SCKEY
Email:
enable: true
server: smtp.exmail.qq.com # 填写邮件的smtp服务器
port: 465 # 填写邮件服务器的端口号
account: '' # 邮件服务器登录用户名
password: '' # 邮件服务器登录密码

上面代码需要修改的地方有这几点(注意 :1.yaml语法格式的原因,修改内容时应该从冒号后面空一格开始,如下面举的例子 2.填写学号和密码应该填在单引号的里面 3.不同学院的收集内容可能不同,因此需要修改上面的问题)

1
school: 燕山大学 #这里冒号和燕山大学之间有一个空格

type的值为2时,说明这道题是一道单选题,为1时,说明这是一道填空题;注意,在设置温度时,一定要带上度,这样它就是一个string类型,不是一个int类型

1
2
3
4
- default:
type: 2
title: (是/否)去医院就诊
value:

改好这个config.yml后,保存,并且部署,点击上面的层管理,再点击绑定选择刚才nihao(我的是nihao,你的取决于你当时取得名字啦)确定即可

然后选择左边的触发管理->创建触发器,配置如下图,如果想要选其他时间(每个学院发布收集的时间不一致嘛),请您观察规律,自行修改😂,不建议选整点,因为其实这是GitHub上那位大佬的服务器,选整点的人多了,自然服务器也累😂,效果也不好,做完这些提交就行啦!

好的,现在我们可以回到刚刚修改代码的的地方,点击一下测试

您得到的日志可能是这样子的,这表示您可能成功了,也可能没成功,因此,真正是否成功,还是要等到导员发了信息采集才可以,令人满意的效果应该像这种样子

这是我第一次通过本地成功时的截图
这是我第一次通过本地成功时的截图

OK,如果您已经完成这些,那么您就应该成功(当然,希望您还是再观察几天,以免出错),如果没有成功,欢迎在下方留言(留言无需注册,留个mail就行,留言支持图片,直接将图片拖拽进去就行),如果你想要感谢这个项目,请点击这里:auto-submit赞助它的原作者即可,下面我分享一下的我在将这个项目修改的时遇到的一些bug(小白直接跳过)

过程分享

我本来在了解这个项目时,我对腾讯云函数并不了解,于是首先我尝试在本地先测试一下,在安装python的第三方库时,各种报错,这里我十分建议将源换成清华源,然后关掉所有代理,如果wifi下载失败,建议改用流量。我本以为只要改config.yml里的内容就行,但无奈又报错了

观察日志最后,发现是requests超时Max retries exceeded with url: /v6/config/guest/tenant/list 又由上面的位置可知,这个url在index.py里,于是我找到了这一行,复制了url在chrome里访问了一下

显然url失效了,上网搜了一下,原来由于作者更新过时,最后在csdn里面找到了两个新的网址

1
2
https://static.campushoy.com/apicache/tenantListSort
https://mobile.campushoy.com/v6/config/guest/tenant/info?ids=ysu

但是很不幸,网址一返回的json文件是字典里data的value是列表,而且列表里还有字典,字典里有datas,而这里requests.get的schools的遍历无法满足下面的判断。

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
# 获取今日校园api
def getCpdailyApis(user):
apis = {}
user = user['user']
schools = requests.get(
url='https://www.cpdaily.com/v6/config/guest/tenant/list', verify=not debug).json()['data']
flag = True
for one in schools:
if one['name'] == user['school']:
if one['joinType'] == 'NONE':
log(user['school'] + ' 未加入今日校园')
exit(-1)
flag = False
params = {
'ids': one['id']
}
res = requests.get(url='https://www.cpdaily.com/v6/config/guest/tenant/info', params=params,
verify=not debug)
data = res.json()['data'][0]
joinType = data['joinType']
idsUrl = data['idsUrl']
ampUrl = data['ampUrl']
if 'campusphere' in ampUrl or 'cpdaily' in ampUrl:
parse = urlparse(ampUrl)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host

ampUrl2 = data['ampUrl2']
if 'campusphere' in ampUrl2 or 'cpdaily' in ampUrl2:
parse = urlparse(ampUrl2)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host
break
if flag:
log(user['school'] + ' 未找到该院校信息,请检查是否是学校全称错误')
exit(-1)
log(apis)
return apis

于是我就想着给它剥洋葱了😂,搜了一些剥洋葱的教程后,代码这样了(其实很简单,大家也不要随便放弃哟!)

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
def getCpdailyApis(user):
apis = {}
user = user['user']
schools = requests.get(
url='https://static.campushoy.com/apicache/tenantListSort', verify=not debug).json()['data']
flag = True
for item in schools:
new=item['datas']
for one in new:
#print(one)
if one['name'] == user['school']:
flag = False
params = {
'ids': one['id']
}
res = requests.get(url='https://mobile.campushoy.com/v6/config/guest/tenant/info', params=params,
verify=not debug)
data = res.json()['data'][0]
joinType = data['joinType']
idsUrl = data['idsUrl']
ampUrl = data['ampUrl']
if 'campusphere' in ampUrl or 'cpdaily' in ampUrl:
parse = urlparse(ampUrl)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host

ampUrl2 = data['ampUrl2']
if 'campusphere' in ampUrl2 or 'cpdaily' in ampUrl2:
parse = urlparse(ampUrl2)
host = parse.netloc
res = requests.get(parse.scheme + '://' + host)
parse = urlparse(res.url)
apis[
'login-url'] = idsUrl + '/login?service=' + parse.scheme + r"%3A%2F%2F" + host + r'%2Fportal%2Flogin'
apis['host'] = host
break
if flag:
log(user['school'] + ' 未找到该院校信息,请检查是否是学校全称错误')
exit(-1)
log(apis)
return apis

修改的其实只是这里:

1
2
3
for item in schools:
new=item['datas']
for one in new:

遇到的第二个大问题首先是我以为原作者采用的是findall直接找出关键字,但是当我偷懒只写了一些字符时,又报错了

我只好乖乖将那个问题都填完整(您知道的,有的问题真的好长,打字也容易错),后来改完上传时,表是填完了,但是上传失败。

这时候我有点绝望了,我看到GitHub作者上issue的问题,可惜大家大多数用的都是通用方法:cookie登录,电脑安装逍遥模拟器,然后装 xposed ,电脑fiddler抓包(但是我以前下载过逍遥,体验不好,不想再下载了),issue里面大多数都是说那个加密问题,但是我上文的这种方式其实不完全是模拟app,刚开始我以为是post时没有加上咋们的那个地址没有经纬度的原因,但是我加上去没有效果,于是我打算将通用方法的user移植过来(下面为通用的config.yml),但是加了还是不行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
user:
#username 学号或者工号
username:
#tellphone 电话号码
tellphone:
#address 地址,定位信息
address:
#email 接受通知消息的邮箱
email:
#lon 当前位置经度,可以访问http://zuobiao.ay800.com/s/27/index.php获取
lon: '14.300060'
#lat 当前位置纬度
lat: '332.880656'
#school 学校全称
school: 燕山大学
#今日校园相关配置

所以没有那么那一步后来再那个issue里看到了一位大佬分享的版本密钥,于是我抱着侥幸的心理,修改了一下index.py,成功了。


Server 酱

​ 不知道是我的设置问题还是别的什么问题,在写完这篇教程后的一天,我的server 酱就post成功不了了,但是我在本地测试,也就是用我电脑自带的python3.9却可以成功,我原以为是第三方库的问题,因为我同学也向我反映过这个问题,但是我还是认为它报错存在问题,因为整个index.py中依赖了许多库,如果不能够引入那些库,就不可能完成整个填报,而我只是出现了无法让Server 酱通知

​ 我最近在家里尝试很多次,但是都没有结果,甚至出现了一件让我感到很amazing的事情,就是我点击运行,它能够post成功,但是我点击测试,他就无法成功。

这让我百思不得其解,也让我忧心忡忡,因为如果某一天失败了(或者老师改了问卷),但我却不知道,那岂不是糟糕了,通知还是必要的呀,我想过用短信api来代替,但是又要注册啥的,我就算了,恰好今天看到了一个可以代替Server 酱——企业微信(点击跳转注册),注册完成后在app management里新建一个app

记住下面这两个参数然后点击My Company 记住Company ID

点击wechat workplace然后微信扫描这个二维码即可绑定应用。

然后在config.yml最后一行里面加上这个三个参数格式如下

1
2
3
AgentId: #
Secret: #
CompanyId: #

index.py中找到sendServerChan将这个函数替换为下面代码,配置即可成功,

1
2
3
4
5
6
7
8
9
10
11
def sendServeChan(msg):
url='https://api.htm.fun/api/Wechat/text_card/'
data={
'corpid':config['CompanyId'],
"corpsecret":config['Secret'],
"agentid":config['AgentId'],
"title":"今日校园",
"description":"{}".format(msg),
"url":"/"
}
requests.post(url=url,data=data)

OK,如果这些都配置好了,那么你的wechat应该就可以收到信息了

总结

​ 至此,这个有趣的项目就弄完了,虽然在一天半时间,这个过程中我曾想过放弃很多次,但是考虑到每天填那个表,还是坚持了下载,但终究还是我运气好,就成功了,欢迎大家有问题啥的留言呀😂,最后分享一下喜悦

如果还想实现其他的功能建议自己去探索哟,比如拍照签到,因为我没有相关的条件(没有拍照签到的任务,无法测试😂)