前言

发现大学群里好多代刷网课的,确实觉得好多大学网课好费时间,而且没啥用,刚好用来实战一下学的Python,就当练手了。省下来的时间不是又能多敲几行代码😃

一边省时间,还能学技能,这还不学起来

一、长江雨课堂

1.分析视频请求

F12抓包

Python自动刷网络学堂 python 刷网课_Python自动刷网络学堂

播放过程中只有hearbest一个请求,可以确定这个应该就是我们要找的

请求是POST格式,data数据是一个json,通过多个heatbest可以看出是多个观看记录

截取一个分析如下:

c: 1561016
cards_id: 2027472
cc: ""
classroomid: 9763037
cp:424.6
d: 485.4
et: "playing"
fp: 304.5
i: 5
lob: "ykt"
n:"ali-cdn.xuetangx.com"
p:"web"
pg:"2027472_16s0j"
skuid: ""
slide: 44
sp:2
sq: 54
t:"ykt_cards"
tp: 304.5
ts: "1667910647346"
u: 35359786
uip: ""
v: 2027472
v_url: "rain://xtvideo/id/1901324024"

没发现什么加密的字段,那就一个一个处理
复制第一个1561016,在请求里搜索

Python自动刷网络学堂 python 刷网课_ide_02


也只有一个请求里搜索到扫一眼可以知道这是课程的列表数据

地址

https://changjiang.yuketang.cn/v2/api/web/courses/list?identity=2 请求里也莫有加密,后面有用到的我们提取保存一下

c的意思应该是class(班级)

get_classroom_id = "https://changjiang.yuketang.cn/v2/api/web/courses/list?identity=2"
classroom_id_response = requests.get(url=get_classroom_id, headers=headers)
	for ins in json.loads(classroom_id_response.text)["data"]["list"]:
	     your_courses.append({
	         "course_name": ins["course"]["name"],
	         "classroom_id": ins["classroom_id"],
	         "course_sign": ins["university_course_series_id"],
	         "course_id": ins["course"]["id"]
	     })

card_id同上搜索也只有一个请求

地址: https://changjiang.yuketang.cn/v2/api/web/logs/learn/9763037?actype=-1&page=0&offset=20&sort=-1

9763037搜索就可以发现是classroom_id,已经保存过了

Python自动刷网络学堂 python 刷网课_ide_03


Python自动刷网络学堂 python 刷网课_json_04

就是一个课程列表数据
和上面一样有用的数据保存一下

def get_videos_ids(course_name,classroom_id,course_sign):
    get_homework_ids = "https://changjiang.yuketang.cn/v2/api/web/logs/learn/"+str(classroom_id)+"?actype=-1&page=0&offset=20&sort=-1"
    homework_ids_response = requests.get(url=get_homework_ids, headers=headers)
    homework_json = json.loads(homework_ids_response.text)
    homework_dic = {}
    try:
        for i in homework_json["data"]["activities"]:
            if i['type'] == 14:
                # 课堂
                pass
            elif i['type'] == 9:
                # 公告
                pass
            elif i['type'] == 2:
                # 课件
                homework_dic[i['title']] = {'courseware_id' : i['courseware_id'],
                                            'id' : i['id']
                                            }
        print(course_name+"共有"+str(len(homework_dic))+"个作业喔!")
        return homework_dic

KEY

Value

说明

cp

424.6

观看时长(破解点)

d:

485.4

视频长度

et

“playing”

操作

fp

304.5

常量

i

5

常量

lob

“ykt”

常量

n

“ali-cdn.xuetangx.com”

常量

p

“web”

(观看方式)常量

pg

“2027472_16s0j”

算个小加密可能

skuid

“”

常量

slide

44

视频所在ppt页数

sp

2

常量

sq

54

常量

t

“ykt_cards”

常量

tp

304.5

常量

ts

“1667910647346”

13位时间戳

u

35359786

user_id

uip

“”

常量

v

2027472

cards_id

v_url

“rain://xtvideo/id/1901324024”

地址

差不多都是请求里搜索然后找位置保存,没有什么变化,只有pg后面的一小串要在js里面找,但是是个随机产生的数,可以写死。

可以下一个xhr断点

Python自动刷网络学堂 python 刷网课_ide_05


段下来之后一个一个看调用堆栈

到resetTimer函数的时候,可以看到参数的构建位置

也可以在这里下断点看一下都是怎么构建的

因为我要找pg, 看到

pg: e.options.id + "_" + u

所以就向上找u

Python自动刷网络学堂 python 刷网课_json_06


15760行有定义

var t, i, r, o, n, d, u = Math.floor(1048576 * (1 + Math.random())).toString(36);

2.代码实现

ok所用参数都找到获取方式了接下来就是写代码的时间

# -*- coding: utf-8 -*-
# version 4
# developed by zk chen
import time
import requests
import re
import json
import execjs

class jsFun:
    def __init__(self):
        pass
    def math(self):
        fun = '''
        function a(){
            let u = Math.floor(1048576 * (1 + Math.random())).toString(36);
            return u;};
        '''
        ctx = execjs.compile(fun)
        return ctx.call("a")
# 以下的cookie复制下面图片中的内容!!!!而且脚本需在登录雨课堂状态下使用
Cookie = "这里复制粘贴Cookie"
# 以下字段不用改,下面的代码也不用改动
user_id = ""
f = jsFun()
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42',
    'Content-Type': 'application/json',
    'Cookie': Cookie
    'authority':'changjiang.yuketang.cn',
    'method':'GET',
    'path':'/v2/api/web/courses/list?identity=2',
    'referer':'https://changjiang.yuketang.cn/v2/web/personal/info',
    'sec-fetch-dest':'empty',
    'sec-fetch-mode':'cors',
    'sec-fetch-site':'same-origin',
    # 'university-id':'2727',
    'x-csrftoken':'LE6xYWZ4rWN3UFHHDFkiykMBvffI4sCf',
    'dnt' : '1'
}

leaf_type = {
    "video": 0,
    "homework": 6,
    "exam": 5,
    "recommend": 3,
    "discussion": 4
}

def get_video_id_list(cards_id,classroomid):
    url = f'https://changjiang.yuketang.cn/v2/api/web/cards/detlist/{cards_id}?classroom_id={classroomid}'
    js = json.loads(requests.get(url=url, headers=headers).text)
    List = []
    num =1
    for ins in js['data']['Slides']:
        for i in ins['Shapes']:
            if 'playurl' in i:
                if i['file_type'] == 1:
                    List.append((i['URL'],str(num)))
        num += 1
    return List , num

def one_video_watcher(video_name,cid,user_id,classroomid,cards_id):
    classroomid = str(classroomid)
    id_list,num = get_video_id_list(cards_id,classroomid)
    for url_id,n in id_list:
        url = "https://changjiang.yuketang.cn/video-log/heartbeat/"
        get_url = f"https://changjiang.yuketang.cn/video-log/get_video_watch_progress/?user_id={user_id}&cid={cid}&video_type=ykt_cards&classroom_id={classroomid}&cards_id={cards_id}&v_url={url_id}&slide={n}"
        headers['xtbz'] = 'ykt'
        progress = requests.get(url=get_url, headers=headers)
        if_completed = '0'
        # while 1:
        try:
            js = json.loads(progress.text)[str(n) + ":"+re.search(r'[0-9]+',url_id).group()]
            videolength = js['video_length']
        except:
            print("视频" + str(n) + " " + video_name + "学习失败!")
            continue

                # time.sleep(10)
                # continue
        try:
            if_completed = re.search(r'"completed":(.+?),', progress.text).group(1)
        except:
            pass
        if if_completed == '1':
            print(video_name+"已经学习完毕,跳过")
            continue
        else:
            print(video_name+",尚未学习,现在开始自动学习")
        video_frame = 0
        val = 0
        learning_rate = 20
        t = time.time()
        timestap = int(round(t * 1000))
        while val != "1.0" and val != '1':
            heart_data = []
            for i in range(50):
                heart_data.append(
                    {
                        "i": 5,
                        "et": "loadeddata",
                        "p": "web",
                        "n": "ali-cdn.xuetangx.com",
                        "lob": "ykt",
                        "cp": video_frame,
                        "fp": 0,
                        "tp": 0,
                        "sp": 1,
                        "ts": str(timestap),
                        "u": int(user_id),
                        "uip": "",
                        "c": cid,
                        "v": cards_id,
                        "skuid": '',
                        "classroomid": classroomid,
                        "cc": '',
                        "d": videolength,
                        "pg": f"{cards_id}_"+f.math(),
                        "sq": 2,
                        "t": "ykt_cards",
                        "cards_id": cards_id,
                        "slide": n,
                        "v_url": url_id
                    }
                )
                video_frame += learning_rate
                max_time = int((time.time() + 3600) * 1000)
                timestap = min(max_time, timestap+1000*15)
            data = {"heart_data": heart_data}
            r = requests.post(url=url,headers=headers,json=data)
            try:
                error_msg = json.loads(r.text)["message"]
                if max_time< timestap+1000*15:
                    video_frame = 0
            except:
                pass
            try:

                delay_time = re.search(r'Expected available in(.+?)second.', r.text).group(1).strip()
                print("由于网络阻塞,万恶的雨课堂,要阻塞" + str(delay_time) + "秒")
                time.sleep(float(delay_time) + 0.5)
                video_frame = 0
                print("恢复工作啦~~")
                r = requests.post(url=submit_url, headers=headers, data=data)
            except:
                pass
            progress = requests.get(url=get_url,headers=headers)
            tmp_rate = re.search(r'"rate":(.+?)[,}]',progress.text)
            if tmp_rate is None:
                return 0
            val = tmp_rate.group(1)
            print("学习进度为:" + str(float(val)*100) + "%/100%" + " last_point: " + str(video_frame))
            time.sleep(0.7)
        print("视频"+str(n)+" "+video_name+"学习完成!")
    return 1

def get_videos_ids(course_name,classroom_id,course_sign):
    get_homework_ids = "https://changjiang.yuketang.cn/v2/api/web/logs/learn/"+str(classroom_id)+"?actype=-1&page=0&offset=20&sort=-1"
    homework_ids_response = requests.get(url=get_homework_ids, headers=headers)
    homework_json = json.loads(homework_ids_response.text)
    homework_dic = {}
    try:
        for i in homework_json["data"]["activities"]:
            if i['type'] == 14:
                # 课堂
                pass
            elif i['type'] == 9:
                # 公告
                pass
            elif i['type'] == 2:
                # 课件
                homework_dic[i['title']] = {'courseware_id' : i['courseware_id'],
                                            'id' : i['id']
                                            }
        print(course_name+"共有"+str(len(homework_dic))+"个作业喔!")
        return homework_dic
    except:
        print("fail while getting homework_ids!!! please re-run this program!")
        raise Exception("fail while getting homework_ids!!! please re-run this program!")

if __name__ == "__main__":
    your_courses = []

    # 首先要获取用户的个人ID,即user_id,该值在查询用户的视频进度时需要使用
    user_id_url = "https://changjiang.yuketang.cn/v/course_meta/user_info"

    id_response = requests.get(url=user_id_url, headers=headers)
    id_response.encoding="utf-8"
    try:
        user_id = re.search(r'"user_id": ([0-9]+),', id_response.text).group(1)
    except:
        print("也许是网路问题,获取不了user_id,请试着重新运行")
        raise Exception("也许是网路问题,获取不了user_id,请试着重新运行!!! please re-run this program!")

    # 然后要获取教室id
    get_classroom_id = "https://changjiang.yuketang.cn/v2/api/web/courses/list?identity=2"
    submit_url = "https://gruestc.yuketang.cn/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id=3194"
    headers["referer"] = 'https://changjiang.yuketang.cn/v2/web/index?date=1666686775760&newWeb=1'
    classroom_id_response = requests.get(url=get_classroom_id, headers=headers)
    try:
        for ins in json.loads(classroom_id_response.text)["data"]["list"]:
            your_courses.append({
                "course_name": ins["course"]["name"],
                "classroom_id": ins["classroom_id"],
                "course_sign": ins["university_course_series_id"],
                "course_id": ins["course"]["id"]
            })
    except Exception as e:
        print("fail while getting classroom_id!!! please re-run this program!")
        raise Exception("fail while getting classroom_id!!! please re-run this program!")

    # 显示用户提示
    for index, value in enumerate(your_courses):
        print("编号:"+str(index+1)+" 课名:"+str(value["course_name"]))
    number = input("你想刷哪门课呢?请输入编号。输入0表示全部课程都刷一遍\n")
    if int(number)==0:
        #0 表示全部刷一遍
        for ins in your_courses:
            homework_dic = get_videos_ids(ins["course_name"],ins["classroom_id"], ins["course_sign"])
            for one_video in homework_dic.items():
                one_video_watcher(one_video[0],ins["course_id"],user_id,ins["classroom_id"],cards_id=homework_dic[one_video])
    else:
        #指定序号的课程刷一遍
        number = int(number)-1
        homework_dic = get_videos_ids(your_courses[number]["course_name"],your_courses[number]["classroom_id"],your_courses[number]["course_sign"])
        for one_video in homework_dic.items():
            one_video_watcher(one_video[0], your_courses[number]["course_id"], user_id, your_courses[number]["classroom_id"],cards_id = one_video[1]['courseware_id'])

cookie替换一下

Python自动刷网络学堂 python 刷网课_Python自动刷网络学堂_07

总结