智慧职教改版和遇到的问题Mooc(icve-mooc)API分析
本帖最后由 冉勇爱购物 于 2022-12-26 10:03 编辑## 首先非常感谢此为作者:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1710666&highlight=%D6%C7%BB%DB%D6%B0%BD%CC 提供的思路
## 说一下我遇到的问题吧:
```python
第一:目前和作者一样,使用的cookie登录,随之带来的问题就是,如果制作批量登录的脚本,如何无切割的拿到cookie,或者有更好的办法可以提供给我,有偿作为答谢。
第二:目前还有考试,随堂测验 不知道如何编写。
```
## 我也是基于此代码进行了功能增加
```python
import os
import random
from time import sleep
from bdb import set_trace
from common import init_mooc
from bs4 import BeautifulSoup
import re
import json
import sys
import requests
# 国际商务谈判
BaseURL = "https://course.icve.com.cn"
# 考试url
ExamURL = "https://spoc-exam.icve.com.cn"
# 课程id
courseId = "1754b2c1a83f4268a668e959b9d3941a"
# 评价课程id
subjectId = "89008fd3260bd552a84da2fdc2ebd3a8"
# 评价内容
content = ['非常好', '非常非常的好', '课程精彩,通熟易懂', '非常好 课程里的知识很丰富']
header = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
'Accept-Encodign': 'gzip, deflate, br',
'Cookie': 'JSESSIONID=56E23126CF7ADBE59F22C72E2BBC8F54.learnspace_taolun_aliyun_001; _abfpc=1d466485af98ed5add63780f7b4ace668b0502d5_2.0; cna=d0ed267474e1abe89b79166eab802eac; ssxmod_itna=eqAxBC0Qq7qWuDBPr97QGQtIO7D0DCq2W7nDDsqtrDSxGKidDqxBWWl2He9v9vhiAClo2oDgnTP0Nfb9l74h=5llWeDHxY=DUpDTeqrDeW=D5xGoDPxDeDAQKiTDY4DdjpNv=DEDeKDRDAQDzwd/4h6z/G=DI3iDmTLDx7t9ITL5qeG2DGUeIkGx7qDMIeGXC0nPxDUTTZwMIMuixYPWQk0xxBQD7di9DYoUneDHzdN8ghDW0Gm10iQ5WOxqt+G+7Gme/RhA/GGi7xqLdYeGYr+1b5DAYDj1CiD=; ssxmod_itna2=eqAxBC0Qq7qWuDBPr97QGQtIO7D0DCq2WYikIqqhDlphxjb+xj8drKju5QqL3QD6mYmtQjBPeuDwjW3jAqvee4Yv8eCKdYcftOKCDXesC+iF4OqlKUycajU8B1dQ2BWuHqOQfcS6q/24ax9DdEc5C2mKCYIyGZY7GPhrOWp74jCvGErqCPKwh+pvGq0YN+Rqa0pAGW3BaQfHGL9bx0WkCclYaAF5QlC2hDH3202n7OIeTKAjiQO8F6cIhNgLoDQFODjKD+a95=nxbMAQOYpDK4D=; _uid=10b64d0f-5e25-46df-aa14-9e89b0f28624; aliyungf_tc=c9ec8cdfbda8bcd917ef59b8ff4bdd109baf6ad818c5b9c097917d41d0d78f5b; UNTYXLCOOKIE=dXNlci5pY3ZlLmNvbS5jbnx8MWRjZjNmMWY3OTI3NjM2MTVmYmNjM2Q3MDlmMjBhNDN8fDEzMjA2MjY5ODA0fHx6aHpq; learning-course=ms32amyox4hk6j6arvwmra_1754b2c1a83f4268a668e959b9d3941a___; jwplayer.volume=50; JSESSIONID=0E83027FAC1E707AFE9032C602BF79BF.learnspace_kfkc_aliyun; h_courseId=1754b2c1a83f4268a668e959b9d3941a; platform_flag=learnspace; learnspace_taolun=e546ee25771cb793b3354ca277163e90; sid=6a8bfaf1-d01e-4793-a638-082109d758b6; token=91d9c9b5-ed6d-4883-81d2-99061f7029b6; rest_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbklkIjoiMTMyMDYyNjk4MDQiLCJzaXRlQ29kZSI6InpoemoiLCJ1c2VyX25hbWUiOiIxMzIwNjI2OTgwNCIsInBob3RvIjoibnVsbCIsInN1SWQiOiJtczMyYW15b3g0aGs2ajZhcnZ3bXJhIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9TVFVERU5UIl0sImNsaWVudF9pZCI6IjIxMDgxODc3MTgiLCJ0cnVlTmFtZSI6IuWGieWLhyIsInN1ZElkIjoibXMzMmFteW94NGhrNmo2YXJ2d21yYSIsInJvbGVDb2RlIjoiMCIsInNjb3BlIjpbImFsbCJdLCJyb2xlTmFtZSI6IuWtpueUnyIsImV4cCI6MTY3MjczNzg5OSwianRpIjoiNGY4MmQxMTctNjUzZS00M2FiLWI5MTgtMGVjNDVmZGYwYzgzIn0.Gb4Busjaqk3MSlPOOI8ItxhhQqnHG3C4c2cMK5YDBUF1Ruy2skFyPizAosvhWr5RtbnQU17YIajICNJgfnXhL9XR7rLR1SN2c6eO1MfLPuQELNKxnCTz8nLD23cyLiL637BliH9U7CEhgTceEaSyYWHqbfbp-j_TEhIzUudRY4-yHsuNCyTirvZHVjIR6k81vTf5rlq6sqnkHvHVJzsWExgoKreq03Ckugyz0mY6qonZnJDQ-f3X403N-bZZrfQyeJis29cXbsyUeNtLO_ckdyfeYzdXED7_wIEtk0lNxRMI5uuE7rCI2DFVVlSQw0cPQzjxA-MAO2kDstHx_Ud1QA; acw_tc=2f6fc11d16715300837038833e5dd1ba0168d2f4c95dd67e8d555ae8b32fde; SERVERID=f25cd604b9c4c08dc3b80e49f8c85d21|1671530184|1671507410; ST="xZP0QArbMp99EGZ29ba5pUFcLdaSL44hklZdXZx9UXI="'
}
# 获取limitId
res = requests.get(
url=f'{BaseURL}/learnspace/learn/learn/templateeight/index.action?params.courseId={courseId}___¶ms.templateType=8¶ms.templateStyleType=0¶ms.template=templateeight¶ms.classId=¶ms.tplRoot=learn',
headers=header)
patter = re.compile('limitId.*;')
try:
limitId = patter.search(res.content.decode()).group().split('"')
except:
print('\033[31m获取limitId失败,检查Cookie\033[0m')
exit()
# 获得itemId
res = requests.get(
f'{BaseURL}/learnspace/learn/learn/templateeight/courseware_index.action?params.courseId={courseId}___',
headers=header
)
soup = BeautifulSoup(res.content, 'lxml')
# print("soup:", soup)
# ----------------------------------分割线----------------------------------
# 判断视频
divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="video")
# 视频的id
itemids = {}
for i in divs:
itemids = i['id'].strip("s_point_")
print("获取所有视频id===>", itemids, "开始刷课===>")
# print("itemids.keys():", itemids.keys())
# 开始刷课
for key in itemids.keys():
itemid = itemids
print("itemid:", itemid)
itemid = itemids
data2 = {
'itemId': itemid,
'videoTotalTime': '00:10:00'
}
total = requests.post(url='https://course.icve.com.cn/learnspace/course/plugins/cloud_updateVideoTotalTime.action',
headers=header, data=data2)
# 判断视频是否学习完成
data2 = {
'params.courseId': f'{courseId}___',
'params.itemId': itemid
}
complete = requests.post(
url=f'{BaseURL}/learnspace/learn/learnCourseware/getSingleItemCompleteCase.json',
headers=header, data=data2)
print("code:", complete.content.decode())
# 判断返回值 1表示学习完成,2表示部分学习,0表示内容没有学习过
if json.loads(complete.content.decode())['result']['completed'] == '1':
print(key, '视频状态已完成,跳过')
continue
# 刷课
start = 0
end = 0
# 轮询片段
while True:
start = end
# 每次增加10秒
end = start + 20
while True:
cmd = os.popen('node ./test.js %s %s %s' % (itemid, start, end))
# 原始字符串的开头和结尾删除给定的字符
studyrecord = cmd.read().strip('\n')
cmd.close()
data = {
"limitId": limitId,
"studyRecord": studyrecord
}
res2 = requests.post(
url=f'{BaseURL}/learnspace/course/study/learningTime_saveVideoLearnDetailRecord.action',
headers=header, data=data
)
if '保存成功' in res2.content.decode() or '总时长' in res2.content.decode():
print("\r", end="")
print(key, "\033[32m学习时长: {}秒 \033[0m".format(end), end="")
sys.stdout.flush()
break
else:
pass
if '总时长' in res2.content.decode():
break
print(key, '\033[31m学习完成\033[0m')
# 判断文档
divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="doc")
itemids = {}
for i in divs:
itemids = i['id'].strip("s_point_")
# 轮询item
print("获取所有文档id===>", itemids, "开始刷文档===>")
for key in itemids.keys():
itemid = itemids
# 判断文档是否学习完成
data2 = {
'params.courseId': f'{courseId}___',
'params.itemId': itemid
}
complete = requests.post(
url=f'{BaseURL}/learnspace/learn/learnCourseware/getSingleItemCompleteCase.json',
headers=header, data=data2)
# 判断返回值 1表示学习完成,2表示部分学习,0表示内容没有学习过
if json.loads(complete.content.decode())['result']['completed'] == '1':
print(key, '状态已完成,跳过')
continue
# 保存文档
doc_data = {
'courseId': f'{courseId}',
'itemId': itemid,
'recordType': 0,
'studyTime': 300
}
response = requests.post(
url=f'{BaseURL}/learnspace/course/study/learningTime_saveCourseItemLearnRecord.action',
headers=header, data=doc_data)
if '成功' in response.content.decode():
print(key, '完成')
else:
print(key, '保存失败')
set_trace()
# 判断文本
divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="text")
# print(divs)
itemids = {}
for i in divs:
itemids = i['id'].strip("s_point_")
# 轮询item
print("获取所有图文id===>", itemids, "开始刷图文===>")
for key in itemids.keys():
itemid = itemids
# 判断文档是否学习完成
data2 = {
'params.itemId': itemid,
'params.courseId': f'{courseId}___'
}
complete = requests.post(
url=f'{BaseURL}/learnspace/learn/learnCourseware/getSingleItemCompleteCase.json',
headers=header, data=data2)
# 判断返回值 1表示学习完成,2表示部分学习,0表示内容没有学习过
if json.loads(complete.content.decode())['result']['completed'] == '1':
print(key, '状态已完成,跳过')
continue
# 保存图文
doc_data = {
'courseId': courseId,
'studyTime': 300,
'itemId': itemid,
'recordType': 0
}
# 判断图文是否学习完成
response = requests.post(
url=f'{BaseURL}/learnspace/course/study/learningTime_saveCourseItemLearnRecord.action',
headers=header, data=doc_data)
if json.loads(complete.content.decode())['result']['completed'] == '1':
print(key, '完成')
else:
print(key, '保存失败')
# set_trace()
# 课程评价
data = {"params": {f"subjectId": f"{subjectId}", "star": 5}}
response = requests.post(
url=f'{BaseURL}/discuss-api/discussStar/saveStar', headers=header, json=data)
if json.loads(response.content.decode())['code'] == '1':
print("进行评价")
create_data = {
"bean": {"subjectId": f"{subjectId}", "content": f"{random.choice(content)}", "thumbImgs": "",
"originalImgs": ""}}
response = requests.post(
url=f"{BaseURL}/discuss-api/discussComment/create",
headers=header, json=create_data
)
if json.loads(response.content.decode())['code'] == '1':
print("课程评价完成!")
else:
print("错误了!", response.content.decode())
else:
print("错误了!", response.content.decode())
# # 主题讨论
# data = {
# "action": "reply",
# "curPage": 43,
# "parentId": '402883e681197106018329f789602031',
# "mainId": '402883e681197106018329f789602031',
# "content": "<p>灌水!!!!!</p>",
# "itemId": "402883a983232378018329f7892a1ddc",
# "courseId": courseId,
# "createUserId": "402883e5811970fc0183f4ae5cbf55e8",
# }
# response = requests.post(
# url='https://course.icve.com.cn/taolun/learn/courseTopicAction.action', headers=header, data=data)
# print(response.content.decode())
# if json.loads(response.content.decode())['success'] == "True":
# print("灌水成功")
# -----------------未开发功能-----------------
# # 判断音频
# divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="audio")
# # 音频的id
# itemids = {}
# for i in divs:
# itemids = i['id'].strip("s_point_")
# print("获取所有音频id===>", itemids, "开始刷音频>>>>")
# # 开始刷音频
# for key in itemids.keys():
# itemid = itemids
# print("itemid:", itemid)
# # 判断音频是否学习完成
# data2 = {
# 'params.courseId': f'{courseId}___',
# 'params.itemId': itemid
# }
# complete = requests.post(
# url=f'{BaseURL}/learnspace/learn/learnCourseware/getSingleItemCompleteCase.json',
# headers=header, data=data2)
# print("code:", complete.content.decode())
# # 判断返回值 1表示学习完成,2表示部分学习,0表示内容没有学习过
# if json.loads(complete.content.decode())['result']['completed'] == '1':
# print(key, '音频状态已完成,跳过')
# continue
# # 刷音频
# print(f"检测到未刷音频{key},开始刷音频")
# while True:
# start = 0
# end = 0
# # 轮询片段
# while True:
# start = end
# # 每次增加10秒
# end = start + 300
# cmd = os.popen('node ./test.js %s %s %s' % (itemid, start, end))
# # 原始字符串的开头和结尾删除给定的字符
# # studyRecord参数就是将数据格式化后序列化再进行AES加密得到的字符串
# studyrecord = cmd.read().strip('\n')
# cmd.close()
# data = {
# "limitId": limitId,
# "studyRecord": studyrecord
# }
# res2 = requests.post(
# url=f'{BaseURL}/learnspace/course/study/learningTime_saveAudioLearnDetailRecord.action',
# headers=header, data=data
# )
# if '请求成功' in res2.content.decode():
# print("\r", end="")
# print(key, "\033[32m学习时长: {}秒 \033[0m".format(end), end="")
# sys.stdout.flush()
# break
# print(key, '\033[31m学习完成\033[0m')
# todo:需要重新解讨论参数
# # 判断讨论
# divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="topic")
# # print(divs)
# itemids = {}
# for i in divs:
# itemids = i['id'].strip("s_point_")
# # 轮询item
# print("获取所有讨论id===>", itemids, "开始刷讨论===>")
# for key in itemids.keys():
# itemid = itemids
# print("itemid--->", itemid)
# data2 = {
# 'currentId': '402883ab80d6633a018330a12d672bbf',
# 'action': "reply",
# 'curPage': 34,
# 'parentId': '402883ab80d6633a018330a12d672bbf',
# 'mainId': '402883ab80d6633a018330a12d672bbf',
# 'content': '<p>11</p>',
# 'itemId': '402883a9832d1365018330a12d481e49',
# 'courseId': '1754b2c1a83f4268a668e959b9d3941a',
# 'createUserId': '402883e5811970fc0183f4ae5cbf55e8',
# 'createUserName': '13206269804',
# 'createNickName': '冉勇',
# 'createPicFileName': 'null',
# 'replyUserId': '402883e48119710601832876cf911f17',
# 'replyUserName': '13983127453',
# 'replyNickName': '金莹',
# 'loginType': 0
# }
# complete = requests.post(
# url=f'{BaseURL}/taolun/learn/courseTopicAction.action',
# headers=header, data=data2)
# print("--->", complete.content.decode())
# todo:需要重新解析考试网址
# # 判断随堂考试
# divs = soup.find_all(id=re.compile("s_point_.*"), itemtype="exam")
# # print(divs)
# itemids = {}
# for i in divs:
# itemids = i['id'].strip("s_point_")
# # 轮询item
# print("获取所有考试id===>", itemids, "开始刷考试===>")
# for key in itemids.keys():
# itemid = itemids
# # 判断测验是否完成
# print(itemid)
# data2 = {
# 'params.itemId': itemid,
# 'params.courseId': f'{courseId}___'
# }
# complete = requests.post(
# url=f'{BaseURL}/learnspace/learn/learnCourseware/getSingleItemCompleteCase.json',
# headers=header, data=data2)
# if json.loads(complete.content.decode())['result']['completed'] == '1':
# print(key, '状态已完成,跳过')
# continue
# # 考试
# print("开始考试---->")
# # todo:考试逻辑
# doc_data = {
# 'courseId': f'{courseId}___',
# 'studyTime': 300,
# 'itemId': itemid,
# }
# # 判断图文是否学习完成
# response = requests.post(
# url=f'{BaseURL}/learnspace/course/study/learningTime_saveLearningTime.action',
# headers=header, data=doc_data)
# if '成功' in response.content.decode():
# print(key, '完成')
# else:
# print(key, '保存失败')
# set_trace()
# todo:
#刷课类型:itemtype="doc(实现)"、exam(待实现)、topic(待实现)、video(实现)、text(实现)、audio(待实现)
```
## 后续截图补充图文
todo:代码没有抽离分化,后续优化 老版的职教云源码要吗 cyxnzb 发表于 2022-12-23 17:57
老版的职教云源码要吗
老版本的我以前写过了,不过现在要求用新的刷了,后面接口也没维护了
https://github.com/ranyong1997/sk 谢谢分享,回帖学习 慢慢学习 谢谢分享,正在学习 大神,求个云课堂智慧职教的刷课方法啊,太多门了,看不过来啊。
慢慢学习 这么长的代码 真厉害! 这个确实非常需要,如果成功希望分享!