校园网及教务处自动登录查询脚本

临近期末放假,两个小脚本奉上。

1.校园网自动登录及离线脚本

1.1 普通版

配置文件config.ini置于同级目录,内容为{"此处填写账号": "此处填写密码"}

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
import re
import hashlib
import json
import time
import requests
import pytesseract
from bs4 import BeautifulSoup
from PIL import Image

header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36'
}


# 校园网登录
class CampusNetAutoLogin:
def __init__(self):
self.base_url = 'http://10.151.0.249'
self.pc_login_url = self.base_url + '/0.htm'
self.pc_logout_url = self.base_url + '/F.htm'
self.mobile_login_url = self.base_url + '/a30.htm'
self.test_url = 'http://www.yangtzeu.edu.cn'

@staticmethod
def login_account():
"""
从文件中获取明文账号和密码
:return: 账号和密码
"""
with open('config.ini', 'r', encoding='utf-8') as fp:
data = fp.read()
info = json.loads(data)
return info

def login_campus(self):
"""
登录校园网
:return: 登录提示
"""
info = self.login_account()
for username, password in info.items():
# 校园网登录账户密码加密方式
password = hashlib.md5(('2' + password + '12345678').encode(encoding='utf-8')).hexdigest() + '123456782'
data = {
'DDDDD': username,
'upass': password,
'R1': '0',
'R2': '1',
'para': '00',
'0MKKey': '123456',
'v6ip': ''
}
res = requests.post(self.pc_login_url, headers=header, data=data)
raw = re.search(r'<title>(.*?)</title>', res.text).group(1)
return raw

def login_status(self):
"""
检查当前电脑是否登录校园网
:return: 联网状态
"""
status_code = requests.get(self.test_url, allow_redirects=False).status_code
status = False if status_code == 302 else True
# 特殊情况注销一次
if not status:
requests.get(self.pc_logout_url, headers=header)
return status

def check_login(self):
"""
根据联网状态选择是否执行自动登录校园网
:return: 登录状态
"""
status = self.login_status()
# '登录成功'为检测到校园网已登录
return '登录成功' if status else self.login_campus()


# 自助服务
class CampusNetManagement:
def __init__(self):
"""
初始化URL
"""
self.req = requests.session()
self.base_url = 'http://10.151.1.4:8080/Self'
self.login_url = self.base_url + '/login'
self.logout_url = self.base_url + '/dashboard/tooffline?sessionid='
self.verifycode_url = self.base_url + '/login/randomCode'
self.post_url = self.base_url + '/login/verify'
self.home_url = self.base_url + '/dashboard'
self.devicelist_url = self.base_url + '/dashboard/getOnlineList'
self.loginhistory_url = self.base_url + '/dashboard/getLoginHistory'

def get_checkcode(self):
"""
获取页面随机生成的校验码
:return: 校验码
"""
html = self.req.get(self.login_url, headers=header).text
bs = BeautifulSoup(html, 'lxml')
checkcode = bs.find('input', attrs={'name': 'checkcode'})['value']
return checkcode

def get_verificationcode(self):
"""
获取页面随机生成的验证码
:return: 验证码
"""
imghtml = self.req.get(self.verifycode_url, headers=header).content
with open('code.png', 'wb') as fp:
fp.write(imghtml)
image = Image.open('code.png')
code = pytesseract.image_to_string(image)
return code

def auto_login(self, username, password):
"""
自动登录
:param username: 用户名
:param password: 密码
:return: 登录状态
"""
checkcode = self.get_checkcode()
code = self.get_verificationcode()
data = {
'foo': '',
'bar': '',
'checkcode': checkcode,
'account': username,
'password': password,
'code': code
}
res = self.req.post(self.post_url, headers=header, data=data)
flag = True if res.url == self.home_url else False
return flag

def get_accountinfo(self):
"""
获取当前用户信息
:return:
"""
result = self.req.get(self.home_url, headers=header).text
bs = BeautifulSoup(result, 'lxml')
name_data = bs.find('div', class_='caption').h4.text
# 用户姓名
name = name_data.split(',')[0]
# 用户账号
account = bs.find_all('div', class_='col-md-9 col-xs-7')[0].span.text
# 套餐类型
status = bs.find_all('div', class_='col-md-9 col-xs-7')[1].span.small.text.strip()
# 已用时长
usedtime_data = bs.find_all('div', class_='col-md-4 col-xs-6 col-xxxs-12')[0].dl.dt.text.strip()
usedtime = list(usedtime_data)[0] + list(usedtime_data)[-2] + list(usedtime_data)[-1]
# 已用流量
usedbytes_data = bs.find_all('div', class_='col-md-4 col-xs-6 col-xxxs-12')[1].dl.dt.text.strip()
usedbytes = list(usedbytes_data)[0] + list(usedbytes_data)[-1]
print('\n● 账户信息:')
print('姓名:{} | 账号:{} | 套餐类型:{} | 已用时长:{} | 已用流量:{}'.format(name, account, status, usedtime, usedbytes))

def get_deviceinfo(self):
"""
获取登录设备信息
:return:
"""
devicelist_data = self.req.get(self.devicelist_url, headers=header).text
devicelist = json.loads(devicelist_data)
sessionId_dict = {}
sessionId_list = []
print('\n● 设备在线信息:')
if devicelist:
for i in range(0, len(devicelist)):
deviceinfo = devicelist[i]
# 设备登录时间
login_time = deviceinfo['loginTime']
# 设备登录ip
login_ip = deviceinfo['ip'].ljust(14, ' ')
# 设备mac地址
login_mac = deviceinfo['mac']
# 注销设备所需参数
sessionId = deviceinfo['sessionId']
# 下载流量(/Kb)
downFlow = deviceinfo['downFlow']
# 上传流量(/Kb)
upFlow = deviceinfo['upFlow']
# 总流量(/M)
totalFlow = str((int(downFlow) + int(upFlow)) / 1024).ljust(8, ' ')
# 设备类型(PC/移动终端)
login_terminal = deviceinfo['terminalType']
sessionId_dict[login_terminal] = sessionId
print(i + 1, login_time, login_ip, login_mac, totalFlow, login_terminal)
sessionId_list.append(sessionId_dict)
else:
print('未查询到在线设备')
return sessionId_list

def get_recenthistory(self):
"""
获取登录历史
:return:
"""
loginhistory_data = self.req.get(self.loginhistory_url, headers=header).text
loginhistory = json.loads(loginhistory_data)
print('\n● 近期上网记录:')
if loginhistory:
for i in loginhistory:
# id
history_id = i[-1]
# 上线时间
login_timestamp = str(i[0])[:10]
login_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(login_timestamp)))
# 注销时间
logout_timestamp = str(i[1])[:10]
logout_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(logout_timestamp)))
# IP地址
ip = i[2].ljust(14, ' ')
# mac地址
mac = i[3]
# 使用时长
tmpused_time = str(i[4]).ljust(4, ' ')
# 使用流量
tmpused_bytes = str(i[5]).ljust(5, ' ')
# 终端类型
useragent = i[-2]
print(history_id, login_time, logout_time, ip, mac, tmpused_time, tmpused_bytes, useragent)
else:
print('未查询到上网记录')

def select_logout(self, sessionId_list):
"""
选择是否进行注销设备操作
:param sessionId_list: 设备列表信息
:return:
"""
sessionId_data = sessionId_list[0]
quantity = str(len(sessionId_data))
if quantity == '2':
n = input('\n当前有2台设备在线,请输入括号里的数字选择注销PC(1)或移动终端(2)或退出(0):')
if n == '1':
sessionId = sessionId_data['#PC']
self.logout_device(sessionId)
elif n == '2':
sessionId = sessionId_data['#移动终端']
self.logout_device(sessionId)
elif n == '0':
exit()
else:
print('\n输入错误!')
self.select_logout(sessionId_list)
else:
device = list(sessionId_data.keys())[0].split('#')[1]
n = input('\n当前有1台设备在线,请输入括号里的数字选择注销' + device + '(1)或退出(0):')
if n == '1':
sessionId = sessionId_data['#' + device]
self.logout_device(sessionId)
elif n == '0':
exit()
else:
print('\n输入错误!')
self.select_logout(sessionId_list)

def logout_device(self, sessionId):
"""
注销设备操作
:param sessionId: 注销设备参数
:return:
"""
logout_url = self.logout_url + str(sessionId)
status_code = self.req.get(logout_url, headers=header).status_code
if status_code == 500:
print('错误!')
else:
device_list = self.req.get(logout_url, headers=header).text
device_list = json.loads(device_list)
if device_list['success']:
print('\n注销成功!')
else:
print('\n错误!')


def run():
"""
主程序
:return:
"""
login = CampusNetAutoLogin()
service = CampusNetManagement()
login_ret = login.check_login()
if login_ret == '登录成功':
print('提示:校园网自动登录成功,正在登录自助服务...')
info = login.login_account()
for username, password in info.items():
login_flag = service.auto_login(username, password)
if login_flag:
print('提示:自助服务自动登录成功\n')
service.get_accountinfo()
service.get_recenthistory()
sessionId_list = service.get_deviceinfo()
print('=' * 100)
if sessionId_list:
n = input('\n检测到设备在线信息,是否进行注销(y/n):')
if n == 'y':
service.select_logout(sessionId_list)
print('=' * 100)
elif n == 'n':
exit()
else:
print('输入错误')
else:
print('提示:自助服务自动登录失败,请检查config.ini文件中的用户名及密码是否正确')
else:
print('提示:校园网自动登录失败,请检查config.ini文件中的用户名及密码是否正确、校园网是否欠费、账号登录设备台数是否限制')


if __name__ == '__main__':
run()

运行效果图

1.2 简化版

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
import requests
import re
import hashlib
import json

header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36'
}

DEVICE = 1
# 设置校园网登录方式:
# 可选:
# 电脑端登录 DEVICE = 1 (默认)
# 移动端登录 DEVICE = 0

def login_campus():
"""
模拟登录校园网
:return:
"""
with open('config.ini', 'r') as fp:
data = fp.read()
info = json.loads(data)
for username, password in info.items():
password = hashlib.md5(('2' + password + '12345678').encode(encoding='utf-8')).hexdigest() + '123456782'
data = {
'DDDDD': username,
'upass': password,
'R1': '0',
'R2': '1',
'para': '00',
'0MKKey': '123456',
}
if DEVICE:
# PC端登录
data['v6ip'] = ''
login_url = 'http://10.151.0.249/0.htm'
print('提示:正在模拟电脑端登录校园网...')
else:
# 移动端登录
data['R6'] = '1'
login_url = 'http://10.151.0.249/a30.htm'
print('提示:正在模拟移动端登录校园网...')
res = requests.post(login_url, headers=header, data=data)
raw = re.search(r'<title>(.*?)</title>', res.text).group(1)
print(username, '校园网' + raw)
# 若提示"校园网信息返回",请检查config.ini文件中的用户名及密码是否正确、校园网是否欠费、账号登录设备台数是否限制等

def login_status():
"""
检查当前电脑是否登录校园网
:return: 联网状态
"""
status_code = requests.get('http://www.yangtzeu.edu.cn', allow_redirects=False).status_code
status = False if status_code == 302 else True
# 及其特殊情况注销
if not status:
requests.get('http://10.151.0.249/F.htm', headers=header)
return status

def check_login():
"""
根据联网状态选择是否执行自动登录校园网
"""
status = login_status()
if status:
print('提示:检测到校园网已登录')
else:
login_campus()

if __name__ == '__main__':
check_login()

运行效果图

2.教务处成绩查询脚本

2.1 普通版

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
import json
import time
import requests
import hashlib
import re
import pytesseract
from bs4 import BeautifulSoup
from PIL import Image
from prettytable import PrettyTable

header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36'
}

class CourseManagement:

def __init__(self):
self.req = requests.session()
self.base_url = 'http://jwc3.yangtzeu.edu.cn'
self.login_url = self.base_url + '/eams/login.action'
self.captcha_url = self.base_url + '/eams/captcha/image.action'
self.home_url = self.base_url + '/eams/home.action'
self.course_search_url = self.base_url + '/eams/teach/grade/course/person!search.action'
self.query_url = self.base_url + '/eams/dataQuery.action'
self.semesterId_url = self.base_url + '/eams/teach/grade/course/person!search.action?semesterId='

# def get_verificationcode(self):
# """
# 获取登录验证码
# :return:验证码
# """
# img_html = self.req.get(self.captcha_url, headers=header).content
# with open('captcha.png', 'wb') as fp:
# fp.write(img_html)
# image = Image.open('captcha.png')
# captcha = pytesseract.image_to_string(image)
# return captcha

def encrypt_pwd(self, password):
"""
明文密码加密作为post数据之一提交
:param password: 用户明文密码
:return: 加密后的密码
"""
html = self.req.get(self.login_url, headers=header).content.decode('utf-8')
random_str = re.search('[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}-', html).group(0)
tmp = random_str + password
encrypted_pwd = hashlib.sha1(tmp.encode(encoding='utf-8')).hexdigest()
return encrypted_pwd

def auto_login(self, username, password):
"""
自动登录教务处
:param username: 用户名
:param password: 密码
:return: 登录状态
"""
encrypted_pwd = self.encrypt_pwd(password)
# captcha = self.get_verificationcode()
data = {
'username': username,
'password': encrypted_pwd,
'encodedPassword': '',
# 'captcha_response': captcha,
'session_locale': 'zh_CN'
}
time.sleep(0.5)
location = self.req.post(self.login_url, headers=header, data=data).url
flag = True if location == self.home_url else False
return flag

def login_userInfo(self):
"""
获取当前登录账号姓名
:return: 用户姓名
"""
html = self.req.get(self.home_url, headers=header).text
bs = BeautifulSoup(html, 'lxml')
banner = bs.find_all('div', class_='banner_area')[1]
name = banner.form.select('a[href="/eams/security/my.action"]')[0].text
return name

def analyse_grades(self):
"""
解析成绩页面,获取成绩相关信息并显示
:return: 成绩列表
"""
self.req.get(self.course_search_url, headers=header).content.decode('utf-8')
data = {
'tagId': '',
'dataType': 'semesterCalendar',
'value': '',
'empty': ''
}
result = self.req.post(self.query_url, headers=header, data=data).text
semesters = re.findall(r'{id:\d+,schoolYear:"\d{4}-\d{4}",name:"[1,2]"}', result)
table = PrettyTable(['学年学期', '课程名称', '课程类别', '学分', '总评成绩', '最终', '绩点'])
table.padding_width = 0
for i in range(len(semesters) - 7, len(semesters)):
fullsem = semesters[i]
reg = re.compile(r'([a-zA-Z]+)')
semester = reg.sub(r'"\1"', fullsem)
semester = json.loads(semester)
semesterId = semester['id']
semester_url = self.semesterId_url + str(semesterId)
html = self.req.get(semester_url, headers=header).text
bs = BeautifulSoup(html, 'lxml')
flag = bs.find('tbody')
time.sleep(0.5)
if not flag.tr.td:
pass
else:
trs = bs.find('table', class_='gridtable').tbody.find_all('tr')
for tr in trs:
tds = tr.find_all('td')
semester = tds[0].text
course_name = tds[3].text.strip()
course_category = tds[4].text
course_credit = tds[5].text
total_score = tds[6].text.strip()
final_score = tds[7].text.strip()
course_point = tds[8].text.strip()
table.add_row([semester, course_name, course_category, course_credit, total_score, final_score, course_point])
if i < len(semesters) - 3:
table.add_row(['-'*13, '-'*42, '-'*10, '-'*6, '-'*10, '-'*6, '-'*6])
print(table)

@staticmethod
def login_info():
"""
从文件中读取用户登录信息
:return: 信息字典
"""
with open('config.ini', 'r') as fp:
data = fp.read()
info = json.loads(data)
return info

def main(self):
"""
自动登录并打印成绩信息
:return:
"""
print('正在尝试登录教务处...')
info = self.login_info()
username = list(info.keys())[0]
password = list(info.values())[0]
flag = self.auto_login(username, password)
if flag:
time.sleep(0.5)
name = self.login_userInfo()
print('登录成功, 欢迎'+name)
print('正在查询成绩...\n')
self.analyse_grades()
else:
print('登录失败,请检查config.ini文件中的账号信息')

if __name__ == '__main__':
test = CourseManagement()
test.main()

运行效果图

2.2 自动监测发送邮件版

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
import json
import os
import requests
import hashlib
import re
import time
import pytesseract
from bs4 import BeautifulSoup
from PIL import Image
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
from email.header import Header

# SENDER ---> 发送方邮箱地址
# SENDER_PWD ---> 发送方邮箱密码
# RECEIVER ---> 接收方邮箱地址
SENDER = ''
SENDER_PWD = ''
RECEIVER = ''

header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36'
}

req = requests.session()
base_url = 'http://jwc3.yangtzeu.edu.cn'
login_url = base_url + '/eams/login.action'
captcha_url = base_url + '/eams/captcha/image.action'
home_url = base_url + '/eams/home.action'
course_search_url = base_url + '/eams/teach/grade/course/person!search.action'
query_url = base_url + '/eams/dataQuery.action'
semesterId_url = base_url + '/eams/teach/grade/course/person!search.action?semesterId='


# def get_verification():
# """
# 获取登录验证码
# :return:验证码
# """
# img_html = req.get(captcha_url, headers=header).content
# with open('captcha.png', 'wb') as fp:
# fp.write(img_html)
# image = Image.open('captcha.png')
# captcha = pytesseract.image_to_string(image)
# return captcha


def encrypt_pwd(password):
"""
明文密码加密作为post数据之一提交
:param password: 用户明文密码
:return: 加密后的密码
"""
html = req.get(login_url, headers=header).content.decode('utf-8')
random_str = re.search('[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}-', html).group(0)
tmp = random_str + password
encrypted_pwd = hashlib.sha1(tmp.encode(encoding='utf-8')).hexdigest()
return encrypted_pwd


def login_account():
"""
从文件中读取用户登录信息
:return: 信息字典
"""
with open('config.ini', 'r') as fp:
data = fp.read()
info = json.loads(data)
return info


def auto_login(username, password):
"""
自动登录教务处
:param username: 用户名
:param password: 密码
:return: 登录状态
"""
password = encrypt_pwd(password)
# captcha = get_verification()
data = {
'username': username,
'password': password,
'encodedPassword': '',
# 'captcha_response': captcha,
'session_locale': 'zh_CN'
}
time.sleep(0.5)
location = req.post(login_url, headers=header, data=data).url
flag = True if location == home_url else False
return flag


def show_username():
"""
获取当前登录账号姓名
:return: 用户姓名
"""
html = req.get(home_url, headers=header).text
bs = BeautifulSoup(html, 'lxml')
banner = bs.find_all('div', class_='banner_area')[1]
name = banner.form.select('a[href="/eams/security/my.action"]')[0].text
return name


def get_grades():
"""
解析成绩页面,获取成绩相关信息并显示
:return: 成绩列表
"""
req.get(course_search_url, headers=header).content.decode('utf-8')
data = {
'tagId': '',
'dataType': 'semesterCalendar',
'value': '',
'empty': ''
}
result = req.post(query_url, headers=header, data=data).text
semesters = re.findall(r'{id:\d+,schoolYear:"\d{4}-\d{4}",name:"[1,2]"}', result)
for i in range(len(semesters) - 7, len(semesters)):
fullsem = semesters[i]
reg = re.compile(r'([a-zA-Z]+)')
semester = reg.sub(r'"\1"', fullsem)
semester = json.loads(semester)
semesterId = semester['id']
semester_url = semesterId_url + str(semesterId)
html = req.get(semester_url, headers=header).text
bs = BeautifulSoup(html, 'lxml')
flag = bs.find('tbody')
time.sleep(0.5)
if not flag.tr.td:
pass
else:
trs = bs.find('table', class_='gridtable').tbody.find_all('tr')
for tr in trs:
tds = tr.find_all('td')
# 学年学期
semester = tds[0].text
# 课程名称
course_name = tds[3].text.strip()
# 课程类别
course_category = tds[4].text
# 学分
course_credit = tds[5].text
# 总评成绩
total_score = tds[6].text.strip()
# 最终
final_score = tds[7].text.strip()
# 绩点
course_point = tds[8].text.strip()
yield semester, course_name, course_category, course_credit, total_score, final_score, course_point


def format_grades(data):
"""
格式化成绩数据
:param data: 成绩生成器
:return: 成绩字典列表
"""
grade_list = []
for i in data:
grade_dict = {
'semester': i[0],
'course_name': i[1],
'course_category': i[2],
'course_credit': i[3],
'total_score': i[4],
'final_score': i[5],
'course_point': i[6]
}
grade_list.append(grade_dict)
return grade_list


def save_file(filename, data):
"""
写入文件操作
:param filename: 文件名
:param data: 写入内容
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(data)


def check_update(current_grades):
"""
检查成绩是否更新
:param current_grades: 当前查询的成绩
:return: 更新的成绩内容
"""
updates = []
if not os.path.exists('grades.txt'):
save_file('grades.txt', str(current_grades))
print('成绩写入成功')
else:
with open('grades.txt', 'r', encoding='utf-8') as f:
previous_grades = f.read()
if previous_grades:
previous_grades = eval(previous_grades)
updates = [i for i in current_grades if i not in previous_grades]
else:
save_file('grades.txt', str(current_grades))
print('成绩写入成功')
return updates


def send_email(title, content):
"""
发送邮件
:param title: 邮件标题
:param content: 邮件内容
"""
try:
msg = MIMEText(content, 'html', 'utf-8')
msg['Subject'] = Header(title, 'utf-8')
msg['From'] = formataddr(['自动检测脚本', SENDER])
msg['To'] = formataddr(['接收方', RECEIVER])
smtp = smtplib.SMTP_SSL('smtp.163.com', 465)
smtp.login(SENDER, SENDER_PWD)
smtp.sendmail(SENDER, [RECEIVER], msg.as_string())
smtp.quit()
print('邮件发送成功!')
except Exception as e:
print('邮件发送失败!', e)


def main():
"""
自动登录检查成绩是否更新并发送邮件通知
:return:
"""
print('=' * 30)
print('当前时间:' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
print('正在尝试登录教务处...')
info = login_account()
username = list(info.keys())[0]
password = list(info.values())[0]
flag = auto_login(username, password)
if flag:
time.sleep(0.5)
name = show_username()
print('登录成功, 欢迎' + name)
print('正在查询成绩...')
grades = get_grades()
current_grades = format_grades(grades)
updates = check_update(current_grades)
if updates:
print('成绩已更新')
print('正在发送邮件...')
show_msg = [i['semester'] + '&nbsp;'*5 + i['course_name'] + ':' + i['final_score'] for i in updates]
content = "<br>".join(show_msg) + '<br><a href="http://jwc3.yangtzeu.edu.cn">点击此处进入教务处查看更多信息</a>'
send_email('成绩更新啦', content)
save_file('grades.txt', str(current_grades))
else:
print('成绩未更新')
req.cookies.clear()
else:
print('登录失败,请检查config.ini文件中的账号信息')


if __name__ == '__main__':
while True:
now = time.strftime("%H:%M:%S", time.localtime())
# 每天12点、20点执行一次
if now in ['12:00:00', '20:00:00']:
main()

运行效果图