Files
Casmooc/秒刷网课.py
2026-07-13 16:59:15 +08:00

331 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import requests
from PIL import Image
from io import BytesIO
import base64
import ddddocr
import time
# 全局Session对象
session = requests.session()
#全局变量用户名密码和观看速度
#读取用户信息文件内容
def read_config_file():
global username, password, speed
with open('config.txt', 'r', encoding='utf-8') as file:
lines = file.readlines()
for line in lines:
if line.startswith('username'):
username = line.split('')[1].strip().replace(" ", "").replace("\n", "")
elif line.startswith('password'):
password = line.split('')[1].strip().replace(" ", "").replace("\n", "")
elif line.startswith('speed'):
speed = line.split('')[1].strip().replace(" ", "").replace("\n", "")
# 模拟登录
def login():
#获取验证码图片地址
url_code = 'https://www.casmooc.cn/server/modules/imgCode/detail'
data_code = {
"source": "codeDetail"
}
# 请求登录接口需要下列请求头
headers = {
'x-access-device-type': 'WEB',
'x-access-origin': 'aHR0cHM6Ly93d3cuY2FzbW9vYy5jbg==',
#解码后为X-Access-Origin: https://www.casmooc.cn应该是固定字段请求头中必须包含这个值
}
response_code = requests.post(url_code, headers=headers, json=data_code)
if response_code.status_code == 200:
# 解析JSON数据
data = response_code.json()
#print(data)
else:
data = {}
print(f"请求失败,状态码:{response_code.status_code}")
uuid = data['data']['uuid']
captcha_url = data['data']['codeBase64']
#print(uuid)
# 去掉前缀 'data:image/jpeg;base64,'
header, encoded = captcha_url.split(',', 1)
# 解码Base64数据
image_data = base64.b64decode(encoded)
# 使用BytesIO将字节数据转换为PIL图像
image = Image.open(BytesIO(image_data))
# 显示图像
#image.show()
# 如果需要保存图像可以使用image.save方法
image.save('Captcha.png')
ocr = ddddocr.DdddOcr()
with open('Captcha.png', 'rb') as f:
img_bytes = f.read()
Captcha_res = ocr.classification(img_bytes)
print("验证码为:", Captcha_res)
# 定义登录URL和验证码URL
login_url = 'https://www.casmooc.cn/server/modules/eosoperator/login'
#captcha_url = 'data:image/jpeg;base64,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'
# 定义登录表单数据
login_data = {
"userID": username, #"dingxr@microsate.com"
"password": password, #"WXWXsstc_2018"
"code": Captcha_res,
"codeUuid": uuid
}
# 发送登录请求
response_login = session.post(login_url, json=login_data, headers=headers)
time.sleep(4)
# 检查是否登录成功
if response_login.status_code == 200 and response_login.json()['code'] == '0':
print("----------登录成功----------")
print("等待系统初始化")
time.sleep(4)
data_login = response_login.json()
# 获取token
token = data_login['data']['token']
else:
print("!!!!!!!!登录失败!!!!!!!!")
token = ''
return token
# 获取需要学习的网课列表
def get_class_list(token):
url = 'https://www.casmooc.cn/server/api/course/myList'
headers = {
'x-access-origin': 'aHR0cHM6Ly93d3cuY2FzbW9vYy5jbg==',
'x-access-token': token
}
#获取当前年份
year = str(time.localtime().tm_year)
myList_data = {
"formData": {
"courseName": "",
"isCompleted": 0, # 获取未学完课程
"year": year
},
"pageData": {
"currentPage": 1, # 获取第一页的课程
"pageSize": 20, # 一页有多少课程默认为5
"sortName": "",
"sortOrder": ""
}
}
response_class = session.post(url, json=myList_data, headers=headers)
time.sleep(4)
# 检查是否获取成功
if response_class.status_code == 200 and response_class.json()['code'] == '0':
print("获取未学习课程列表成功")
myList = response_class.json()
course_list = myList['data']['list']
#print(course_list)
else:
print("获取未学习课程列表失败")
course_list = []
return course_list
#刷课时
def swipe_class(token, course_list):
# 判断课程是否分节,比如上中下
url_chapter = 'https://www.casmooc.cn/server/api/course/chapter'
# 获取观看锁
url_submitLock = 'https://www.casmooc.cn/server/api/study/submitLock'
# 提交观看记录
url_submit = "https://www.casmooc.cn/server/api/study/submit"
# 确认课程id
url_detail = "https://www.casmooc.cn/server/api/course/detail"
headers = {
'x-access-origin': 'aHR0cHM6Ly93d3cuY2FzbW9vYy5jbg==',
'x-access-token': token
}
def submit_and_detail(belongCourseId, courseId, studyLockUUID):
#此处设置观看速度 30为正常速度数字越小速度越快必须为正整数
if speed in ['1', '1.5', '2', '3']:
submit_time = 30 / float(speed)
else:
submit_time = 30
time.sleep(submit_time)
data_submit = {
"belongCourseId": belongCourseId,
"courseId": courseId,
"isRecordAudio": 0,
"lastLearnTime": 0,
"recordDuration": '30',
"studyLockUUID": studyLockUUID
}
data_detail = {
"id": courseId
}
#提交观看记录
response_submit = session.post(url_submit, headers=headers, json=data_submit)
if response_submit.status_code == 200:
# 解析JSON数据
pass
#data = response_submit.json()
#print("提交数据结果\n", data)
else:
print(f"请求失败,状态码:{response_submit.status_code}")
time.sleep(2)
#当前进度信息
response_detail = session.post(url_detail, headers=headers, json=data_detail)
if response_detail.status_code == 200:
# 解析JSON数据
data = response_detail.json()
print("该课程已学习时长(秒)")
print(data['data']['trueStudyTime'])
print("该课程学习进度(百分比)")
trueStudyProgress = data['data']['trueStudyProgress']
print(trueStudyProgress, '%')
if trueStudyProgress > 95.0:
print('该课程学习完毕')
return False
else:
return True
else:
print(f"请求失败,状态码:{response_detail.status_code}")
return True
for course in course_list:
# 刷完一个网课休息一分钟
print('一分钟后开始学习')
time.sleep(60)
courseId = course['courseId']
courseName = course['courseName']
data_chapter = {
"chapterName": "",
"firstCourseId": courseId
}
response_chapter = session.post(url_chapter, headers=headers, json=data_chapter)
#判断视频是否分节, 访问url_chapter若不为空则分节
if response_chapter.json()['data'] != []:
print('该课程需分节学习')
childList_flag = 0 # 用于标记是第几个分节视频
for course_data in response_chapter.json()['data']:
print("正在学习:", course_data['childList'][0]['secondCourseName'])
secondCourseId = course_data['childList'][0]['secondCourseId'] # 分节后的课程ID
lockIndex = int(time.time() * 1000)
data_submitLock = {
"belongCourseId": courseId, # 此处为视频ID 若不分节,此处为空
"courseId": secondCourseId, # 此处ID为具体播放视频ID
"source": "courseVideoComponent", #文档类学习为courseView
"lockIndex": lockIndex, #由播放时的毫秒级时间戳组成
"is1Lock": 1 #文档类学习没有该属性
}
response_submitLock = session.post(url_submitLock, headers=headers, json=data_submitLock)
studyLockUUID = response_submitLock.json()['data']
#开始刷课, Flag判断是否学习完学习完为False
Flag = True
while Flag:
# 此处设置观看速度 30为正常速度数字越小速度越快必须为正整数
if speed in ['1', '1.5', '2', '3']:
submit_time = 30/float(speed)
else:
submit_time = 30
time.sleep(submit_time)
data_submit = {
"belongCourseId": courseId,
"courseId": secondCourseId,
"isRecordAudio": 0,
"lastLearnTime": 0,
"recordDuration": '30',
"studyLockUUID": studyLockUUID
}
# 提交观看记录
response_submit = session.post(url_submit, headers=headers, json=data_submit)
if response_submit.status_code == 200:
# 解析JSON数据
pass
#data = response_submit.json()
#print("提交数据结果\n", data)
else:
print(f"请求失败,状态码:{response_submit.status_code}")
time.sleep(2)
# 当前进度信息
response_chapter = session.post(url_chapter, headers=headers, json=data_chapter)
if response_chapter.status_code == 200:
# 解析JSON数据
data = response_chapter.json()
print("该课程分节已学习时长(秒)")
print(data['data'][childList_flag]['childList'][0]['trueStudyTime'])
print("该课程学习进度(百分比)")
trueStudyProgress = data['data'][childList_flag]['childList'][0]['trueStudyProgress']
print(trueStudyProgress, '%')
if trueStudyProgress > 95.0:
print('该课程学习完毕')
childList_flag += 1 #data列表索引后移
break
else:
continue
else:
print(f"请求失败,状态码:{response_chapter.status_code}")
else:
print('正在学习:', courseName)
data_detail = {
"id": courseId
}
response_detail = session.post(url_detail, headers=headers, json=data_detail)
#判断课程是否属于电子阅读类
if response_detail.status_code == 200:
# 解析JSON数据
enterUrl = response_detail.json()['data']['enterUrl']
#如果是文档
if enterUrl.endswith('.pdf'):
lockIndex = int(time.time() * 1000)
data_submitLock = {
"belongCourseId": '', # 此处为视频ID 若不分节,此处为空
"courseId": courseId, # 此处ID为具体播放视频ID
"lockIndex": lockIndex, # 由播放时的毫秒级时间戳组成
"source": "courseView", # 文档类学习为courseView
}
response_submitLock = session.post(url_submitLock, headers=headers, json=data_submitLock)
studyLockUUID = response_submitLock.json()['data']
# 开始刷课
Flag = True
while Flag:
Flag = submit_and_detail('', courseId, studyLockUUID)
#如果是视频
else:
lockIndex = int(time.time() * 1000)
data_submitLock = {
"belongCourseId": '', # 此处为视频ID 若不分节,此处为空
"courseId": courseId, # 此处ID为具体播放视频ID
"source": "courseVideoComponent", # 文档类学习为courseView
"lockIndex": lockIndex, # 由播放时的毫秒级时间戳组成
"is1Lock": 1 # 文档类学习没有该属性
}
response_submitLock = session.post(url_submitLock, headers=headers, json=data_submitLock)
studyLockUUID = response_submitLock.json()['data']
# 开始刷课
Flag = True
while Flag:
Flag = submit_and_detail('', courseId, studyLockUUID)
else:
print(f"请求失败,状态码:{response_detail.status_code}")
print('无法判断,学习下一个课程')
continue
if __name__ == '__main__':
# 读取文件并设置全局变量
read_config_file()
print(f"用户名: {username}")
print(f"密码: {password}")
print(f"播放速度: {speed}倍速")
#模拟登录获取token
token = login()
if token != '':
#获取课程列表
course_list = get_class_list(token)
swipe_class(token, course_list)
else:
print('模拟登录失败,请检查用户名密码是否正确')