近期经常需要写一些和华师大数据库交互的小工具,比如导出课程表,获取绩点等。这里提供使用 Python 登录数据库的方法,将登录信息使用 request 包记录在一个 request.session 中。

这里涉及到四个函数。

  • main():一个示例的交互逻辑
  • ECNULogin() 主要的登录函数
  • GetCode() 用于获取验证码。这里把手动输入的两行注释掉了,如果不想安装 tenserflow,则可以注释掉下面的识别部分,手动输入验证码。不过华师大的验证码很好识别。目前我还没有遇到 pytesseract 识别失败的情况。
  • GetRSA()RSA 加密登录信息。这里调用的是华师大数据库中的 js 文件,并使用 execjs 来调用内部的 strEnc 函数。
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
from PIL import Image  # 手动输入验证码
import pytesseract # 自动识别验证码
from lxml import etree
import sys
import requests
import getpass
import execjs # 用于加密

# 记录登录信息的 session
s = requests.session()

mainurl = 'https://portal1.ecnu.edu.cn/cas/login?service=http%3A%2F%2Fapplicationnewjw.ecnu.edu.cn%2Feams%2Fhome.action'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',
'Refer': 'https://portal1.ecnu.edu.cn/cas/login?service=http%3A%2F%2Fapplicationnewjw.ecnu.edu.cn%2Feams%2Fhome.action%3Bjs'}

def GetRSA(username, password):
# 获取 des.js 里的内容
jsstr = requests.get('https://portal1.ecnu.edu.cn/cas/comm/js/des.js').text
desJS = execjs.compile(jsstr)
# 调用 strEnc 函数实现 rsa 加密
try:
rsa = desJS.call('strEnc', username + password, '1', '2', '3')
except:
print('加密错误。')
return rsa


def GetCode():
"发送一次新的 get 请求并获取验证码,让用户填写。"
print('正在获取验证码...')
r = s.get(mainurl, headers=headers)
imgraw = s.get('https://portal1.ecnu.edu.cn/cas/code')
with open(sys.path[0] + '/temp.jpg', 'wb+') as f:
f.write(imgraw.content)
# OpenFile(sys.path[0] + '/data/temp.jpg')
# captacha = input('请输入验证码:')
print('正在识别验证码...')
img = Image.open(sys.path[0] + '/temp.jpg')
captacha = pytesseract.image_to_string(img)
print('识别结果: {}'.format(captacha))
return captacha


def ECNULogin(username='', password='', ifEnterPassword=False):
"""
返回值:
0 - 成功登陆
1 - 验证码错误
2 - 密码错误
3 - 未知错误
"""
if (ifEnterPassword == True):
username = input('请输入你的公共数据用户名(学号):')
password = getpass.getpass('请输入你的公共数据库密码(直接输入即可,已关闭输入回显):')
code = GetCode()

# 用户名和密码经过了 RSA 加密。
postData = {
'code': code,
'rsa': GetRSA(username, password),
'ul': len(username),
'pl': len(password),
'lt': 'LT-211100-OG7kcGcBAxSpyGub3FC9LU6BtINhGg-cas',
'execution': 'e1s1',
'_eventId': 'submit'
}
print('正在尝试登录...')
r = s.post(mainurl, data=postData)
elements = etree.HTML(r.content)
errors = elements.xpath('//*[@id="errormsg"]')
if len(errors) == 0:
realName = elements.xpath('//a[contains(@title, "查看登录记录")]/font/text()')[0]
print('登录成功:', realName)
return 0
elif elements.xpath('//*[@id="errormsg"]/text()')[0] == "验证码有误":
return 1
elif elements.xpath('//*[@id="errormsg"]/text()')[0] == "用户名密码错误":
return 2
else:
return 3


def main():
username = input('请输入你的公共数据用户名(学号):')
password = getpass.getpass('请输入你的公共数据库密码(直接输入即可,已关闭输入显示):')
feedback = ECNULogin(username, password, ifEnterPassword=False)
while feedback != 0:
if feedback == 1:
print('验证码识别错误,请重试。')
feedback = ECNULogin(username, password, ifEnterPassword=False)
elif feedback == 2:
print('用户名或密码错误,请重试。')
feedback = ECNULogin(ifEnterPassword=True)
else:
print('未知错误,输入 0 以重试,输入其他任何内容退出。')
c = input()
if (c == '0'):
feedback = ECNULogin(ifEnterPassword=True)
else:
print('无法连接。')


main()

登录成功后,信息就会被记录在 s 中,之后使用该 session 跳转所有校内网站都可以直接登录。

至于可以用来干什么……。

恩。理论上是可以用来抢课的