吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5602|回复: 20
收起左侧

[Web逆向] 智慧职教改版和遇到的问题Mooc(icve-mooc)API分析

  [复制链接]
冉勇爱购物 发表于 2022-12-20 21:32
本帖最后由 冉勇爱购物 于 2022-12-26 10:03 编辑

首先非常感谢此为作者:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1710666&highlight=%D6%C7%BB%DB%D6%B0%BD%CC 提供的思路

说一下我遇到的问题吧:

第一:目前和作者一样,使用的cookie登录,随之带来的问题就是,如果制作批量登录的脚本,如何无切割的拿到cookie,或者有更好的办法可以提供给我,有偿作为答谢。
第二:目前还有考试,随堂测验 不知道如何编写。

我也是基于此代码进行了功能增加

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('"')[1]
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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
print("获取所有视频id===>", itemids, "开始刷课===>")
# print("itemids.keys():", itemids.keys())
# 开始刷课
for key in itemids.keys():
    itemid = itemids[key]
    print("itemid:", itemid)
    itemid = itemids[key]
    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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
# 轮询item
print("获取所有文档id===>", itemids, "开始刷文档===>")
for key in itemids.keys():
    itemid = itemids[key]
    # 判断文档是否学习完成
    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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
# 轮询item
print("获取所有图文id===>", itemids, "开始刷图文===>")
for key in itemids.keys():
    itemid = itemids[key]
    # 判断文档是否学习完成
    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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
# print("获取所有音频id===>", itemids, "开始刷音频>>>>")
# # 开始刷音频
# for key in itemids.keys():
#     itemid = itemids[key]
#     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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
# # 轮询item
# print("获取所有讨论id===>", itemids, "开始刷讨论===>")
# for key in itemids.keys():
#     itemid = itemids[key]
#     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.find(class_="s_pointti").text] = i['id'].strip("s_point_")
# # 轮询item
# print("获取所有考试id===>", itemids, "开始刷考试===>")
# for key in itemids.keys():
#     itemid = itemids[key]
#     # 判断测验是否完成
#     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:代码没有抽离分化,后续优化

免费评分

参与人数 6吾爱币 +6 热心值 +6 收起 理由
hzle + 1 + 1 我很赞同!
h2j + 1 + 1 谢谢@Thanks!
zmeng06 + 1 + 1 hao
Ifyoulike + 1 + 1 谢谢@Thanks!
FlyersYi + 1 + 1 用心讨论,共获提升!
cyh000 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

cyxnzb 发表于 2022-12-23 17:57
老版的职教云源码要吗
 楼主| 冉勇爱购物 发表于 2022-12-26 09:59
cyxnzb 发表于 2022-12-23 17:57
老版的职教云源码要吗

老版本的我以前写过了,不过现在要求用新的刷了,后面接口也没维护了
https://github.com/ranyong1997/sk
ABC9758 发表于 2022-12-22 20:09
mine4ever001 发表于 2022-12-23 12:17
慢慢学习
dadaliya 发表于 2022-12-23 22:53
谢谢分享,正在学习
eerrtr3 发表于 2022-12-25 09:12
大神,求个云课堂智慧职教的刷课方法啊,太多门了,看不过来啊。
nitian0963 发表于 2022-12-25 18:26

慢慢学习
BI3NWQ 发表于 2022-12-26 09:56
这么长的代码 真厉害!
讨论思想 发表于 2022-12-26 13:44
这个确实非常需要,如果成功希望分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-14 23:44

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表