Python爬虫爬取中国海洋大学教务系统课表

Python环境配置

  1. 首先需要requests包进行爬虫代码的编写pip install requests
  2. 由于教务系统登录时使用了rsa非对称加密,所以需要导入包pip install pycryptodome

到现在就可以编写程序了

通过抓包工具获得整个登录流程

可以用浏览器自带的,也可以用Charles等

image-20230326150441872

image-20230326150512223

看起来还是比较让人头大的,基本思路是将上面的关键步骤利用爬虫进行模拟

首先来看登录的基本流程

image-20230326151218186

这里是第一个URL: http://id.ouc.edu.cn:8071/sso/ssoLogin

出于保密我并没有完全截取密码部分

我这里先列举一个111加密出来的密码

XPhhrlYF2nV7N0ufUDKDFDKozbQcTF1wJkt5eQH3UvdBSPiMi8fnEYybx4doBPAAdIfrVRQOC0WwWlh8ijVvikmZw2hpknSK/sF9lZWV3melQXJ/h1nKZfps1RqW0insN6dcKXh7aC4IYYA2j0biLEBwdQXMTAWwHle1lWYMQH0=

这一串非常长的密文(后边带着一个等号)采用的是rsa非对称加密

具体细节可以参考下面这篇文章

具体代码在程序中实现如下:

#登录密码的加密
def encrpt(password, public_key):
public_key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----'
rsakey = RSA.importKey(public_key)
cipher = Cipher_pksc1_v1_5.new(rsakey)
cipher_text = base64.b64encode(cipher.encrypt(password.encode()))
return cipher_text.decode()


# key需要修改成自己的
key = ''
username = input("请输入用户名:")
password = input(("请输入密码"))

# 实例化session对象
session = requests.session()

#登录信息门户

url = 'http://id.ouc.edu.cn:8071/sso/ssoLogin'

data = {
'username': username,
'password': encrpt(password, key),
}

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.44',
}

obj = session.post(url=url, data=data, headers=headers)
if obj.text == '{"state":true}':
print("登录成功")
else:
print("用户名或密码错误")

这里面需要的公钥key可以用抓包工具在文件中找,还是比较好找的,我这里就不公布了。

注意

为了保留cookie,这里使用Session()实例化对象session,以下网络请求均使用session

下面是一个post请求:http://id.ouc.edu.cn:8071/sso/login?service=http%3A%2F%2Fmy.ouc.edu.cn%2Fuser%2FsimpleSSOLogin

携带的参数有四个分别是:

data = {
'username': username,
'password': str(base64.b64encode(password.encode('utf-8')).decode('utf-8')),
'lt': 'e1s1',
'_eventId': 'submit',
}

这里的password采用的是base64加密,需要对应的改一下,下面的lt参数对应请求次数,第一次为e1s1第二次为e2s1(好像是我忘记了)总之,不用动。_eventId也不用动。

下面是GET请求, 状态码302: http://my.ouc.edu.cn/user/simpleSSOLogin?ticket=ST-1491796-v5cKNr4iKrVURebiWIPZ-UIA

当然映入眼帘的最吸引人之处就是这个参数ticket了,怎么来的腻?

答案在上一条请求的响应头中,响应头的location中表明了这个URL,也就是要重定向到的地址,可是当我们尝试去获取这个location的时候出了点问题,我们并没有得到想要的这个URL,而是得到了一个这样的http://id.ouc.edu.cn:8071/sso/login;jsessionid=B5CD01DCF93BC023F72EA52E72AD0CB7?service=http%3A%2F%2Fmy.ouc.edu.cn%2Fuser%2FsimpleSSOLogin

这一步不可以跳过直接访问下一条语句

所以我们通过page = session.post(url=page.url, headers=headers, data=data)按照和刚才一样的方式再访问一般这个URL,这次就可以执行下一条URL请求了。

GET请求:http://my.ouc.edu.cn/web/guest

这样就登录到信息门户了

接下来便是我的课表方案一:

因为教务系统里面有两个样式的课表,分别在不同的地方

POST请求:http://jwgl.ouc.edu.cn/login

登录教务系统

POST请求:http://jwgl.ouc.edu.cn/frame/desk/showLessonScheduleDetailJson.action

得到课表一:

大概样式是这样的

https://image.daoxuan.cc/image/202303262356962.webp

课表方案二

headers = {
'Host': 'jwgl.ouc.edu.cn',
'Upgrade-Insecure-Requests': '1',
'Accept': '*/*',
'Referer': 'http://jwgl.ouc.edu.cn/student/xkjg.wdkb.jsp?menucode=JW130416',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Connection': 'keep-alive'
}

params = {
"params": "eG49MjAyMiZ4cT0yJnhoPTIyMDIwMDA3MDY3"
}

page = session.get(url="http://jwgl.ouc.edu.cn/student/xkjg.ckdgxsxdkchj_data.jsp", params=params, headers=headers)

这里我踩过的坑就是请求头一定要写全,只使用UA伪装是行不通的(泪的教训)

尤其是Referer不能少

这次得到的样式是这样的:

屏幕截图 2023-03-27 000820

两者显示的信息各有不同

课表方案三

就是我们的教务系统自带的二维表

只需要改一下URL就可以了

page = session.get(url="http://jwgl.ouc.edu.cn/student/wsxk.xskcb.jsp", params=params, headers=headers)

这样你就可以得到这样的课表

image-20230327001932738

整体代码多少给一下:

import base64
import requests
import re
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pksc1_v1_5
from Crypto.PublicKey import RSA
import time


#登录密码的加密
def encrpt(password, public_key):
public_key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----'
rsakey = RSA.importKey(public_key)
cipher = Cipher_pksc1_v1_5.new(rsakey)
cipher_text = base64.b64encode(cipher.encrypt(password.encode()))
return cipher_text.decode()


# key需要修改成自己的
key = ''//自己找公钥
username = input("请输入用户名:")
password = input(("请输入密码"))

# 实例化session对象
session = requests.session()

#登录信息门户

url = 'http://id.ouc.edu.cn:8071/sso/ssoLogin'

data = {
'username': username,
'password': encrpt(password, key),
}

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.44',
}

obj = session.post(url=url, data=data, headers=headers)
if obj.text == '{"state":true}':
print("登录成功")
else:
print("用户名或密码错误")

data = {
'username': username,
'password': str(base64.b64encode(password.encode('utf-8')).decode('utf-8')),
'lt': 'e1s1',
'_eventId': 'submit',
}

page = session.post(
url='http://id.ouc.edu.cn:8071/sso/login?service=http%3A%2F%2Fmy.ouc.edu.cn%2Fuser%2FsimpleSSOLogin',
headers=headers, data=data)
page = session.post(url=page.url, headers=headers, data=data)

page = session.get(url='http://my.ouc.edu.cn/web/guest', headers=headers)

#登录教务系统

page = session.get(url='http://jwgl.ouc.edu.cn/login', headers=headers)

# 课表方案一
page = session.post(url='http://jwgl.ouc.edu.cn/frame/desk/showLessonScheduleDetailJson.action', headers=headers)
with open("./课表1.html", "w", encoding='gbk') as fp:
fp.write(page.text)
# print(page.url)

# 课表方案二

headers = {
'Host': 'jwgl.ouc.edu.cn',
'Upgrade-Insecure-Requests': '1',
'Accept': '*/*',
'Referer': 'http://jwgl.ouc.edu.cn/student/xkjg.wdkb.jsp?menucode=JW130416',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Connection': 'keep-alive'
}

params = {
"params": "eG49MjAyMiZ4cT0yJnhoPTIyMDIwMDA3MDY3"
}

page = session.get(url="http://jwgl.ouc.edu.cn/student/xkjg.ckdgxsxdkchj_data.jsp", params=params, headers=headers)


with open("./课表2.html", "w", encoding='gbk') as fp:
fp.write(page.text)


page = session.get(url="http://jwgl.ouc.edu.cn/student/wsxk.xskcb.jsp", params=params, headers=headers)
with open("./课表3.html", "w", encoding='gbk') as fp:
fp.write(page.text)

print("over!!")

不要直接CV大法,如果你是中国海洋大学的学弟,欢迎加入爱特工作室。

如果你已经是爱特的了,欢迎加入程序部。

如果你已经是程序部的了,那你就不要在看了,有些事情要自己发掘才有意思。