项目地址:https://github.com/Jakesoso/WZU_helper
早就听说python在抓取数据方面功能的强大,现手上有一个现成的抓取对象,于是就趁着这个机会学习一下python。
毕竟还没接触过,所以在写的过程中借鉴了一部分代码。限于时间和技术,目前代码只实现了 登录内网门户 登录教务系统 抓取个人课表信息 抓取公选课信息 这几个功能。
运行环境:python3 校园网
import binascii
import re
import requests
from pyDes import ECB, PAD_PKCS5, des
from bs4 import BeautifulSoup
实现原理:
1) 登录内网门户:登录时密码有加密操作,具体思路:参考思路 。这边不赘述,放实现代码。
1.1) login_hall(username, password):
# 登录内网门户
def login_hall(username, password):
session = requests.session()
session.headers = headers
# 访问网址,返回包含认证页面链接的内容(自动跳转)
url = 'https://source.wzu.edu.cn/login'
resp = session.get(url, verify=False)
# 提取认证链接并访问,经历一次重定向得到认证页面,且会返回一个cookie值:session
croypto = re.search(r'"login-croypto">(.*?)<', resp.text, re.S).group(1)
execution = re.search(r'"login-page-flowkey">(.*?)<', resp.text, re.S).group(1)
# 构建post数据 填入自己的学号 密码
data = {
'username': username, # 学号
'type': 'UsernamePassword',
'_eventId': 'submit',
'geolocation': '',
'execution': execution,
'captcha_code': '',
'croypto': croypto, # 密钥 base64格式
'password': encrypt(password, croypto) # 密码 经过des加密 base64格式
}
# 提交cookie,进行登录(重定向)
session.cookies.update({'isPortal': 'false'})
url = 'https://source.wzu.edu.cn/login'
resp = session.post(url, data=data)
# 打印提示
if resp.status_code == 200:
print('成功登录内网门户大厅,请输入数字以继续操作!')
print('1)教务系统')
else:
print('登录失败!检查账号密码,在校园网环境下登录')
# 传出session
return session
1.2)des_encrypt(s, key) 和 def encrypt(pd, key) 解密函数
# des解密
def des_encrypt(s, key):
"""
DES 加密
:param key: 秘钥
:param s: 原始字符串
:return: 加密后字符串,16进制
"""
secret_key = key
k = des(secret_key, mode=ECB, pad=None, padmode=PAD_PKCS5)
en = k.encrypt(s, padmode=PAD_PKCS5)
return en # 得到加密后的16位进制密码 <class 'bytes'>
def encrypt(pd, key):
"""
密码加密过程:
1 从认证页面中可获得base64格式的秘钥
2 将秘钥解码成bytes格式
3 输入明文密码
4 通过des加密明文密码
5 返回base64编码格式的加密后密码
:param pd: 明文密码
:param key: 秘钥
:return: 加密后的密码(base64格式)
"""
key = binascii.a2b_base64(key.encode('utf-8')) # 先解码 <class 'bytes'>
pd_bytes = des_encrypt(pd, key) # 得到加密后的16位进制密码 <class 'bytes'>
pd_base64 = binascii.b2a_base64(pd_bytes, newline=False).decode('utf-8')
return pd_base64
至此,我们成功进入了内网门户,可以进一步登录教务系统。
2) 登录教务系统
2.1) 通过对内网门户的观察,得到教务系统的链接。这边直接进入,代码如下:
url = 'http://hall.wzu.edu.cn/visit/dab247c354d64695a1ae6ad714011fa6'
resp = session.get(url)
2.2) 除此之外,我们还需要获取学生姓名,后面会用到,具体代码如下:
# 获取姓名
name = (re.search(r'"xhxm">(.*?)同学', resp.text, re.S).group(1))
至此,登陆操作完成,全部代码如下:
# 登录教务系统
def login_zhengfang(session):
url = 'http://hall.wzu.edu.cn/visit/dab247c354d64695a1ae6ad714011fa6'
resp = session.get(url)
# 获取姓名
name = (re.search(r'"xhxm">(.*?)同学', resp.text, re.S).group(1))
# 打印提示
if resp.status_code == 200:
print(name + '同学你好' + ',请输入数字以继续操作!')
else:
print('登录失败,请重试!')
3) 公选课查询
3.1) 通过对教务系统的观察,得到公选课界面的链接。
url = 'http://jwc3.wzu.edu.cn/xf_xsqxxxk.aspx?' + 'xh=' + str(username) + '&gnmkdm=N121112'
3.2)访问公选课界面,通过模拟选课,找到发送的数据,具体如下:
data = { '__EVENTTARGET': '', '__EVENTARGUMENT': '', '__VIEWSTATE': viewstate, '__VIEWSTATEGENERATOR': '03DFB912', 'ddl_kcxz': '', 'ddl_ywyl': '', # 有无余量 'ddl_kcgs': '', # 课程归属 'ddl_xqbs': 1, # 校区信息 'ddl_sksj': '', 'TextBox1': '', # 课程搜索 'dpkcmcGrid:txtChoosePage': '1', # 页数 'dpkcmcGrid:txtPageSize': '15', # 每页元素个数
这里,我们需要得到 '__VIEWSTATE' 这一数据的值,我们可以通过get抓取到,代码如下:
resp = session.get(url)
# 获取viewstate
viewstate = re.search(r'name="__VIEWSTATE" value="(.*?)" />', resp.text, re.S).group(1)
3.3)获取公选课信息,通过发送数据,得到返回结果并进行解析。这里不赘述,代码如下:
resp = session.post(url, data=data)
soup = BeautifulSoup(resp.text, 'html.parser')
html_selected = str(soup.findAll('table', id='DataGrid2')) # 已选科目
html_all = str(soup.findAll('table', id='kcmcGrid'))
html_all = re.findall(r'top=60\'\)">(.*?)</a>', html_all, re.S)
至此,公选课信息获取完毕,补上输出方法,全部代码如下:
# 获取公选课信息
def zhengfang_enroll_course(name):
url = 'http://jwc3.wzu.edu.cn/xf_xsqxxxk.aspx?' + 'xh=' + str(username) + '&gnmkdm=N121112'
resp = session.get(url)
# 校区信息(北校区 学院路校区 南校区 温州医科大学 在线学习)
ddl_xqbs = '1'
# 构建post数据,获取符合当前条件的选课
viewstate = re.search(r'name="__VIEWSTATE" value="(.*?)" />', resp.text, re.S).group(1)
data = {
'__EVENTTARGET': '',
'__EVENTARGUMENT': '',
'__VIEWSTATE': viewstate,
'__VIEWSTATEGENERATOR': '03DFB912',
'ddl_kcxz': '',
'ddl_ywyl': '', # 有无余量
'ddl_kcgs': '', # 课程归属
'ddl_xqbs': ddl_xqbs, # 校区信息
'ddl_sksj': '',
'TextBox1': '',
'dpkcmcGrid:txtChoosePage': '1',
'dpkcmcGrid:txtPageSize': '200',
}
# 获取选课科目
resp = session.post(url, data=data)
soup = BeautifulSoup(resp.text, 'html.parser')
html_selected = str(soup.findAll('table', id='DataGrid2')) # 已选科目
html_all = str(soup.findAll('table', id='kcmcGrid'))
html_all = re.findall(r'top=60\'\)">(.*?)</a>', html_all, re.S)
# 输出所有选课结果
print('【科目 老师】')
count = 0
num = 1
for each in html_all:
count += 1
if (count % 2 != 0):
print(num, each, end=' ')
if (count % 2 == 0):
print(each, end='\n')
num += 1
还需说明一点,目前只支持查询,选课操作日后补充。