├── cookie.png ├── .gitignore ├── README.md ├── homeworkHelper.py ├── videoHelper.py └── SingleVideoHelper.py /cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heyblackC/yuketangHelper/HEAD/cookie.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite3 2 | *.pyc 3 | __pycache__ 4 | .idea/ 5 | /*.log 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yuketangHelper 2 | ## 研究生雨课堂网课脚本代码 3 | homeworkHelper.py是雨课堂网课作业刷题自动脚本 4 | SingleVideoHelper.py是长江雨课堂版的视频观看脚本 5 | videoHelper.py是雨课堂网课视频观看自动脚本 6 | 我们雨课堂的网站是:https://gsscut.yuketang.cn/pro/portal/home/ 7 | 最后更新日期:2023年2月20日(有同学反馈说脚本还是能用~) 8 | **截止至21年01月18日,刷题bug被修复了,homeworkHelper.py已经作废** 9 | 10 | ## 须知 11 | - 脚本假定使用者具有基本的计算机知识,懂得cookie,会按F12进入开发者模式,且会运行Python代码 12 | - 两个脚本都是将csrftoken和sessionid更改成自己登录后的cookie中对应的字段后即可运行 13 | ![cookies示例](cookie.png) 14 | - 我知道大佬有很多,请别喷我写的代码,然后也希望一起协同完善呀! 15 | 16 | ## 跨校使用 17 | 1. 更改代码中学校的网站地址:https://xxx.yuketang.cn/....(xxx为自己学校的) 18 | 2. 根据登录后cookie里的university_id值,更改代码中university-id、university_id、uv_id 19 | 感谢@honlu同学的测试 20 | --- 21 | 22 | ## 开发记录 23 | ### videoHelper.py 24 | - v1、v2版本硬编码很多,几乎不能看 25 | - v3版本能够让用户自行选择刷哪门课 26 | - v4版本2s一个视频,1分钟十几个视频,项目管理118个视频15min刷完!快到没朋友! 27 | - 它的优点是速度很快,油猴脚本快闪一边,但也因为太快了,视频的观看完成时间会很接近,请自己思考要不要用脚本 28 | 29 | ### SingleVideoHelper.py 30 | - @Sue1347同学提供 31 | - 长江雨课堂版本,在一些网址上有修改,需要自己填写一些个人信息和课程信息,目前每次只能刷一个课程 32 | 33 | ### homeworkHelper.py 34 | - v1、v2版本甚至无法在别人电脑上运行orz 35 | - v3版本仅能刷项目管理概论作业题,由我独立开发 36 | - v4版本由素未相识的李同学友情改良,可以刷用户名下所有的课程的线上作业(非常感谢杨同学和他的舍友) 37 | - V5版本旨在跨学院使用,兼容了填空题,另外增加了交互,可以选择想刷哪个课程 38 | - 寿终正寝啦,已经无法使用 39 | 40 | 41 | 42 | --- 43 | 44 | ## 讲点故事 45 | > 非技术向 46 | 47 | 最开始产生要写脚本的想法,是在上论文写作课,我看着一堆待完成的视频头疼的要死,于是按出F12查看网页网络流动,用postman去尝试发get或post请求。虽然朋友说雨课堂应该不会有bug的,我还是继续探索了一阵子。无它,就是觉得好玩。被恶作剧一般的心理驱使着,想着要是能写出自动刷视频的脚本并帮助他人,那该有多好。 48 | 49 | 后来发现了通过调整last_point参数可以提高视频的完成率,于是有了最初videoHelper的雏形。磨蹭了几天写出来后,强行让身边的同学在我电脑上登他们的账号帮他们刷完了视频。这个视频的脚本我没敢发到群里,玩了几次后这件事渐渐被我搁置了。 50 | 51 | 12月8日的时候,同学们在班级群讨论项目管理的作业很多(65个),视频也很多(118个)。我也打开了项目管理的作业,看着超出屏幕长度的作业,一股无名怒火冲上脑袋,我按下了F12。一番探索后,我很意外地发现了bug(或者是后门),我招呼自己的舍友都来看了一遍这个好笑bug:后台接口把作业答案也放在json字段中返回了。也就是说,我们只需要把返回的答案再上传到服务器,就能完成作业并拿满分。自然而然地,我基于上述想法开始写自动完成作业的脚本。我是学计算机的,我希望能用编程解放自己的双手。 52 | 53 | 12月9日凌晨,我把最初的v1作业脚本发在了班群里,但第二天我就发现v1脚本换个用户根本运行不了,我有点慌,返回实验室抓紧完善脚本,并请了一位好友帮忙测试,直到好友告诉我脚本很"完美",我才松了一口气,把v3版本的作业脚本发到班群中。直到现在我也不知有没有人被v1版的脚本坑了。如果有,你又恰好在看这段话,那请收下我诚挚的道歉。 54 | 55 | 我想脚本应该是有帮到大家的忙的,知道这一点,我就已经很开心了。但出乎我意料的是,就在9号晚上,杨同学毫无预兆地发出了作业脚本v4,说这是他的舍友李某某改进的。我当时刚夜跑完在走回宿舍,看着消息一路边走边笑:一个根本就不认识的朋友,帮忙改进了代码。我想这就是对代码的热爱吧。李同学有一个很重要的贡献,是发现了leaf_type字段对应视频、作业、考试等不同类型的数据,这是我所忽视掉的东西。我代码写的不好,他花时间看懂了并改进,非常的厉害。 56 | 57 | 然后我把脚本发给在微电子学院的朋友,让他们帮忙测试跨学院通用性。没曾想到他们的专业课中有填空题这种类型,原版没有适配填空题的代码把我好朋友的做题页面搞崩了,服务器提示500,后来经过调查我们发现填空题的答案key应该是answers,而其他的是answer,乱传字段会导致页面崩溃。于是,在12月12日,我将作业代码完善到了v5版本。我同学的壮烈牺牲促成了这一版本的出现(x),哈哈开个玩笑,后来我带我同学去实验室,一顿操作把服务器500给修复好了。 58 | 59 | 再后面不断有知情人找我拿刷视频脚本,于是我也在一下一下的"催更"中基本完善了刷视频脚本videoHelper的v3,我很感谢这些找我拿脚本的朋友。说真的,有人用我写的脚本,我就已经很开心了。这一个仓库呢,也只是留作纪念而已,不敢奢望能有多少个star,有几个人协作,能获得多少使用。 60 | 61 | 我在想,也许明天雨课堂就修复好了作业题的bug,那么homeworkHelper直接作废。 62 | 63 | 我在想,也许雨课堂明天就加强了异常访问检测,把爬虫行为检测出来封禁掉,那么videoHelper直接无效。 64 | 65 | 但起码在此时此刻,它们是有用且有帮助的。 66 | 67 | 2020.12.15 于实验室 68 | 69 | --- 70 | 71 | 2020.12.18 更新 72 | 73 | 今天突然想到可以把heart_data字段中的内容多个合并为单个json数据一齐发出去,这样就能大大提高刷课速度了!于是我根据这个idea开发了v5的视频脚本。现在没有什么视频是1个请求搞不定的(如果有,就2个请求)哈哈哈。 74 | 75 | --- 76 | 77 | 2021.01.18 更新 78 | 79 | 貌似yuketang的接口不再直接返回答案了,好日子过不久呀,homeworkHelper.py作废了。这个仓库算是我第一个真正意义上对他人有用的开源项目,以此留作纪念吧。最初我也没想过会让这个idea一步步从个人使用走到班级使用,再到跨学院使用,最后到跨校使用。不管怎样,这是很好玩的一次经历。 80 | -------------------------------------------------------------------------------- /homeworkHelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # version:5.0 3 | # developed by zk chen and MR.Li 4 | # V3版本仅能刷项目管理概论作业题 5 | # V4版本由李同学改良,可以刷用户名下所有的课程的线上作业 6 | # V5版本旨在跨学院使用,在微电子学院网课中发现了填空题类型,因此兼容了填空题,另外增加了交互,可以选择想刷哪个课程 7 | import time 8 | import requests 9 | import re 10 | import json 11 | 12 | # 以下的csrftoken和sessionid需要改成自己登录后的cookie中对应的字段!!!!而且脚本需在登录雨课堂状态下使用 13 | # 登录上华工研究生雨课堂,然后按F12-->选Application-->找到它的cookies,寻找csrftoken和sessionid字段,并复制到下面两行即可 14 | csrftoken = "yours" #需改成自己的 15 | sessionid = "yours" #需改成自己的 16 | 17 | # 会自动跳过已经完成的题目,无须担心,如果运行一遍后,仍有遗漏,再次运行即可。 18 | # 因为作业答案在网页接口中返回了,因此本脚本才能自动答题 19 | headers = { 20 | 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0', 21 | 'Content-Type': 'application/json;charset=UTF-8', 22 | 'Cookie': 'csrftoken=' + csrftoken + '; sessionid=' + sessionid + '; university_id=3078; platform_id=3', 23 | 'x-csrftoken': csrftoken, 24 | 'sec-fetch-dest': 'empty', 25 | 'sec-fetch-mode': 'cors', 26 | 'sec-fetch-site': 'same-origin', 27 | 'university-id': '3078', 28 | 'xtbz': 'cloud', 29 | } 30 | 31 | leaf_type = { 32 | "video": 0, 33 | "homework": 6, 34 | "exam": 5, 35 | "recommend": 3, 36 | "discussion": 4 37 | } 38 | 39 | def do_homework(submit_url, classroom_id, course_sign, course_name): 40 | # second, need to get homework ids 41 | get_homework_ids = "https://gsscut.yuketang.cn/mooc-api/v1/lms/learn/course/chapter?cid="+str(classroom_id)+"&term=latest&uv_id=3078&sign="+course_sign 42 | homework_ids_response = requests.get(url=get_homework_ids, headers=headers) 43 | homework_json = json.loads(homework_ids_response.text) 44 | homework_ids = [] 45 | try: 46 | for i in homework_json["data"]["course_chapter"]: 47 | for j in i["section_leaf_list"]: 48 | if "leaf_list" in j: 49 | for z in j["leaf_list"]: 50 | #print(z['leaf_type'], z['name'], z['id']) 51 | if z['leaf_type'] == leaf_type["homework"]: 52 | print(z['name'], z['leaf_type'], leaf_type["homework"], z['id']) 53 | homework_ids.append(z["id"]) 54 | else: 55 | if j['leaf_type'] == leaf_type["homework"]: 56 | homework_ids.append(j["id"]) 57 | print(course_name+"共有"+str(len(homework_ids))+"个作业喔!") 58 | print(homework_ids) 59 | except: 60 | print("fail while getting homework_ids!!! please re-run this program!") 61 | raise Exception("fail while getting homework_ids!!! please re-run this program!") 62 | 63 | # finally, we have all the data needed 64 | for homework in homework_ids: 65 | get_leaf_type_id_url = "https://gsscut.yuketang.cn/mooc-api/v1/lms/learn/leaf_info/"+str(classroom_id)+"/"+str(homework)+"/?term=latest&uv_id=3078" 66 | leaf_response = requests.get(url=get_leaf_type_id_url, headers=headers) 67 | try: 68 | leaf_id = json.loads(leaf_response.text)["data"]["content_info"]["leaf_type_id"] 69 | except: 70 | continue 71 | problem_url = "https://gsscut.yuketang.cn/mooc-api/v1/lms/exercise/get_exercise_list/"+str(leaf_id)+"/?term=latest&uv_id=3078" 72 | id_response = requests.get(url=problem_url, headers=headers) 73 | dictionary = json.loads(id_response.text) 74 | for pro in dictionary["data"]["problems"]: 75 | if pro["user"]["is_show_answer"]: 76 | continue 77 | answer = "" 78 | answer_key = "" 79 | # 有的问题可能是填空题,比较难搞,它的key为answers 80 | try: 81 | if pro["content"]["Type"].find("FillBlank") < 0: 82 | answer = pro["user"]["answer"] 83 | answer_key = "answer" 84 | else: 85 | answer = pro["user"]["answers"] 86 | for key,value in answer.items(): 87 | if isinstance(value,list): 88 | answer[key] = value[0] 89 | answer_key = "answers" 90 | except: 91 | print("问题类型很奇怪,安全起见跳过~") 92 | continue 93 | submit_json_data = { 94 | "classroom_id": int(classroom_id), 95 | "problem_id": pro["content"]["ProblemID"], 96 | answer_key: answer 97 | } 98 | response = requests.post(url=submit_url, headers=headers, data=json.dumps(submit_json_data)) 99 | # 有可能网络阻塞,要延迟30s 100 | print(response.text) 101 | try: 102 | delay_time = re.search(r'Expected available in(.+?)second.',response.text).group(1).strip() 103 | print("由于网络阻塞,万恶的雨课堂,要阻塞" +str(delay_time)+"秒") 104 | time.sleep(float(delay_time)+0.5) 105 | print("恢复工作啦~~") 106 | response = requests.post(url=submit_url, headers=headers, data=json.dumps(submit_json_data)) 107 | except: 108 | pass 109 | time.sleep(0.5) 110 | print(dictionary["data"]["name"] + "已经完成!!满分!!!") 111 | 112 | if __name__ == "__main__": 113 | your_courses = [] 114 | course = {} 115 | 116 | # first, need to get classroom_id 117 | get_classroom_id = "https://gsscut.yuketang.cn/mooc-api/v1/lms/user/user-courses/?status=1&page=1&no_page=1&term=latest&uv_id=3078" 118 | submit_url = "https://gsscut.yuketang.cn/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id=3078" 119 | classroom_id_response = requests.get(url=get_classroom_id, headers=headers) 120 | try: 121 | for ins in json.loads(classroom_id_response.text)["data"]["product_list"]: 122 | # print(ins["course_name"]) 123 | course_name = ins["course_name"] 124 | classroom_id = ins["classroom_id"] 125 | course_sign = ins["course_sign"] 126 | your_courses.append({ 127 | "course_name": course_name, 128 | "classroom_id": classroom_id, 129 | "course_sign": course_sign 130 | }) 131 | except Exception as e: 132 | print("fail while getting classroom_id!!! please re-run this program!") 133 | raise Exception("fail while getting classroom_id!!! please re-run this program!") 134 | for index, value in enumerate(your_courses): 135 | print("编号:"+str(index+1)+" 课名:"+str(value["course_name"])) 136 | number = input("你想刷哪门课呢?请输入编号。输入0表示全部课程都刷一遍\n") 137 | if int(number)==0: 138 | for ins in your_courses: 139 | do_homework(submit_url, ins["classroom_id"], ins["course_sign"], ins["course_name"]) 140 | else: 141 | number = int(number)-1 142 | do_homework(submit_url, your_courses[number]["classroom_id"], your_courses[number]["course_sign"], your_courses[number]["course_name"]) 143 | 144 | -------------------------------------------------------------------------------- /videoHelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # version 4 3 | # developed by zk chen 4 | import time 5 | import requests 6 | import re 7 | import json 8 | 9 | # 以下的csrftoken和sessionid需要改成自己登录后的cookie中对应的字段!!!!而且脚本需在登录雨课堂状态下使用 10 | # 登录上雨课堂,然后按F12-->选Application-->找到雨课堂的cookies,寻找csrftoken和sessionid字段,并复制到下面两行即可 11 | csrftoken = "yours" #需改成自己的 12 | sessionid = "yours" #需改成自己的 13 | 14 | # 以下字段不用改,下面的代码也不用改动 15 | user_id = "" 16 | 17 | headers = { 18 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36', 19 | 'Content-Type': 'application/json', 20 | 'Cookie': 'csrftoken=' + csrftoken + '; sessionid=' + sessionid + '; university_id=3078; platform_id=3', 21 | 'x-csrftoken': csrftoken, 22 | 'sec-fetch-dest': 'empty', 23 | 'sec-fetch-mode': 'cors', 24 | 'sec-fetch-site': 'same-origin', 25 | 'university-id': '3078', 26 | 'xtbz': 'cloud' 27 | } 28 | 29 | leaf_type = { 30 | "video": 0, 31 | "homework": 6, 32 | "exam": 5, 33 | "recommend": 3, 34 | "discussion": 4 35 | } 36 | 37 | def one_video_watcher(video_id,video_name,cid,user_id,classroomid,skuid): 38 | video_id = str(video_id) 39 | classroomid = str(classroomid) 40 | url = "https://gsscut.yuketang.cn/video-log/heartbeat/" 41 | get_url = "https://gsscut.yuketang.cn/video-log/get_video_watch_progress/?cid="+str(cid)+"&user_id="+user_id+"&classroom_id="+classroomid+"&video_type=video&vtype=rate&video_id=" + str(video_id) + "&snapshot=1&term=latest&uv_id=3078" 42 | progress = requests.get(url=get_url, headers=headers) 43 | if_completed = '0' 44 | try: 45 | if_completed = re.search(r'"completed":(.+?),', progress.text).group(1) 46 | except: 47 | pass 48 | if if_completed == '1': 49 | print(video_name+"已经学习完毕,跳过") 50 | return 1 51 | else: 52 | print(video_name+",尚未学习,现在开始自动学习") 53 | video_frame = 0 54 | val = 0 55 | learning_rate = 20 56 | t = time.time() 57 | timestap = int(round(t * 1000)) 58 | while val != "1.0" and val != '1': 59 | heart_data = [] 60 | for i in range(50): 61 | heart_data.append( 62 | { 63 | "i": 5, 64 | "et": "loadeddata", 65 | "p": "web", 66 | "n": "ws", 67 | "lob": "cloud4", 68 | "cp": video_frame, 69 | "fp": 0, 70 | "tp": 0, 71 | "sp": 1, 72 | "ts": str(timestap), 73 | "u": int(user_id), 74 | "uip": "", 75 | "c": cid, 76 | "v": int(video_id), 77 | "skuid": skuid, 78 | "classroomid": classroomid, 79 | "cc": video_id, 80 | "d": 4976.5, 81 | "pg": "4512143_tkqx", 82 | "sq": 2, 83 | "t": "video" 84 | } 85 | ) 86 | video_frame += learning_rate 87 | max_time = int((time.time() + 3600) * 1000) 88 | timestap = min(max_time, timestap+1000*15) 89 | data = {"heart_data": heart_data} 90 | r = requests.post(url=url,headers=headers,json=data) 91 | print(r.text) 92 | try: 93 | error_msg = json.loads(r.text)["message"] 94 | if "anomaly" in error_msg: 95 | video_frame = 0 96 | except: 97 | pass 98 | try: 99 | delay_time = re.search(r'Expected available in(.+?)second.', r.text).group(1).strip() 100 | print("由于网络阻塞,万恶的雨课堂,要阻塞" + str(delay_time) + "秒") 101 | time.sleep(float(delay_time) + 0.5) 102 | video_frame = 0 103 | print("恢复工作啦~~") 104 | r = requests.post(url=submit_url, headers=headers, data=data) 105 | except: 106 | pass 107 | progress = requests.get(url=get_url,headers=headers) 108 | tmp_rate = re.search(r'"rate":(.+?)[,}]',progress.text) 109 | if tmp_rate is None: 110 | return 0 111 | val = tmp_rate.group(1) 112 | print("学习进度为:" + str(float(val)*100) + "%/100%" + " last_point: " + str(video_frame)) 113 | time.sleep(0.7) 114 | print("视频"+video_id+" "+video_name+"学习完成!") 115 | return 1 116 | 117 | def get_videos_ids(course_name,classroom_id,course_sign): 118 | get_homework_ids = "https://gsscut.yuketang.cn/mooc-api/v1/lms/learn/course/chapter?cid="+str(classroom_id)+"&term=latest&uv_id=3078&sign="+course_sign 119 | homework_ids_response = requests.get(url=get_homework_ids, headers=headers) 120 | homework_json = json.loads(homework_ids_response.text) 121 | homework_dic = {} 122 | try: 123 | for i in homework_json["data"]["course_chapter"]: 124 | for j in i["section_leaf_list"]: 125 | if "leaf_list" in j: 126 | for z in j["leaf_list"]: 127 | if z['leaf_type'] == leaf_type["video"]: 128 | homework_dic[z["id"]] = z["name"] 129 | else: 130 | if j['leaf_type'] == leaf_type["video"]: 131 | # homework_ids.append(j["id"]) 132 | homework_dic[j["id"]] = j["name"] 133 | print(course_name+"共有"+str(len(homework_dic))+"个作业喔!") 134 | return homework_dic 135 | except: 136 | print("fail while getting homework_ids!!! please re-run this program!") 137 | raise Exception("fail while getting homework_ids!!! please re-run this program!") 138 | 139 | if __name__ == "__main__": 140 | your_courses = [] 141 | 142 | # 首先要获取用户的个人ID,即user_id,该值在查询用户的视频进度时需要使用 143 | user_id_url = "https://gsscut.yuketang.cn/edu_admin/check_user_session/" 144 | id_response = requests.get(url=user_id_url, headers=headers) 145 | try: 146 | user_id = re.search(r'"user_id":(.+?)}', id_response.text).group(1).strip() 147 | except: 148 | print("也许是网路问题,获取不了user_id,请试着重新运行") 149 | raise Exception("也许是网路问题,获取不了user_id,请试着重新运行!!! please re-run this program!") 150 | 151 | # 然后要获取教室id 152 | get_classroom_id = "https://gsscut.yuketang.cn/mooc-api/v1/lms/user/user-courses/?status=1&page=1&no_page=1&term=latest&uv_id=3078" 153 | submit_url = "https://gsscut.yuketang.cn/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id=3078" 154 | classroom_id_response = requests.get(url=get_classroom_id, headers=headers) 155 | try: 156 | for ins in json.loads(classroom_id_response.text)["data"]["product_list"]: 157 | your_courses.append({ 158 | "course_name": ins["course_name"], 159 | "classroom_id": ins["classroom_id"], 160 | "course_sign": ins["course_sign"], 161 | "sku_id": ins["sku_id"], 162 | "course_id": ins["course_id"] 163 | }) 164 | except Exception as e: 165 | print("fail while getting classroom_id!!! please re-run this program!") 166 | raise Exception("fail while getting classroom_id!!! please re-run this program!") 167 | 168 | # 显示用户提示 169 | for index, value in enumerate(your_courses): 170 | print("编号:"+str(index+1)+" 课名:"+str(value["course_name"])) 171 | number = input("你想刷哪门课呢?请输入编号。输入0表示全部课程都刷一遍\n") 172 | if int(number)==0: 173 | #0 表示全部刷一遍 174 | for ins in your_courses: 175 | homework_dic = get_videos_ids(ins["course_name"],ins["classroom_id"], ins["course_sign"]) 176 | for one_video in homework_dic.items(): 177 | one_video_watcher(one_video[0],one_video[1],ins["course_id"],user_id,ins["classroom_id"],ins["sku_id"]) 178 | else: 179 | #指定序号的课程刷一遍 180 | number = int(number)-1 181 | homework_dic = get_videos_ids(your_courses[number]["course_name"],your_courses[number]["classroom_id"],your_courses[number]["course_sign"]) 182 | for one_video in homework_dic.items(): 183 | one_video_watcher(one_video[0], one_video[1], your_courses[number]["course_id"], user_id, your_courses[number]["classroom_id"], 184 | your_courses[number]["sku_id"]) 185 | -------------------------------------------------------------------------------- /SingleVideoHelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # version 4 3 | # developed by zk chen 4 | import time 5 | import requests 6 | import re 7 | import json 8 | 9 | ''' 10 | 长江雨课堂版本,在一些网址上有修改。 11 | 需要自己填写以下的个人信息和课程信息, 12 | 而且,,,每次只能刷一个课程。 13 | 14 | (没有测试过网络拥塞的情况) 15 | 2021.10 16 | ''' 17 | 18 | # 以下的csrftoken和sessionid需要改成自己登录后的cookie中对应的字段!!!!而且脚本需在登录雨课堂状态下使用 19 | # 登录上雨课堂,然后按F12-->选Application-->找到雨课堂的cookies,寻找csrftoken和sessionid字段,并复制到下面两行即可 20 | csrftoken = "" # 需改成自己的 21 | sessionid = "" # 需改成自己的 22 | 23 | # 对单个课程的 video helper 24 | user_id = "" # 需改成自己的 25 | sku_id = "" # 需改成自己的 26 | course_id = "" # 需改成自己的 27 | course_name = "" # 需改成自己的 28 | classroom_id = "" # 需改成自己的 29 | 30 | 31 | headers = { 32 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36', 33 | # 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36', 34 | 'Content-Type': 'application/json', 35 | 'Cookie': 'csrftoken=' + csrftoken + '; sessionid=' + sessionid + '; university_id=3078; platform_id=3', 36 | 'x-csrftoken': csrftoken, 37 | 'sec-fetch-dest': 'empty', 38 | 'sec-fetch-mode': 'cors', 39 | 'sec-fetch-site': 'same-origin', 40 | 'university-id': '3078', 41 | 'xtbz': 'cloud' 42 | } 43 | 44 | leaf_type = { 45 | "video": 0, 46 | "homework": 6, 47 | "exam": 5, 48 | "recommend": 3, 49 | "discussion": 4 50 | } 51 | 52 | 53 | def one_video_watcher(video_id, video_name, cid, user_id, classroomid, skuid): 54 | video_id = str(video_id) 55 | classroomid = str(classroomid) 56 | url = "https://changjiang.yuketang.cn/video-log/heartbeat/" 57 | get_url = "https://changjiang.yuketang.cn/video-log/detail/?cid=" + str( 58 | cid) + "&user_id=" + user_id + "&classroom_id=" + classroomid + "&video_type=video&vtype=rate&video_id=" + str( 59 | video_id) + "&snapshot=1" # &term=latest&uv_id=3078 60 | progress = requests.get(url=get_url, headers=headers) 61 | 62 | if_completed = '0' 63 | try: 64 | if_completed = re.search(r'"completed":(.+?),', progress.text).group(1) 65 | except: 66 | pass 67 | if if_completed == '1': 68 | print(video_name + "已经学习完毕,跳过") 69 | return 1 70 | else: 71 | print(video_name + ",尚未学习,现在开始自动学习") 72 | video_frame = 0 73 | val = 0 74 | learning_rate = 20 75 | t = time.time() 76 | timestap = int(round(t * 1000)) 77 | while val != "1.0" and val != '1': 78 | heart_data = [] 79 | for i in range(50): 80 | heart_data.append( 81 | { 82 | "c": cid, 83 | "cc": video_id, 84 | "classroomid": classroomid, 85 | "cp": video_frame, 86 | "d": 643.7, # 4976.5, 87 | "et": "heartbeat", 88 | "fp": 0, 89 | "i": 5, 90 | "lob": "ykt", 91 | "n": "ali-cdn.xuetangx.com", 92 | "p": "web", 93 | "pg": "6611328_18kfd", # 4512143_tkqx 94 | "skuid": skuid, 95 | "sp": 1, # speed of the video 96 | "sq": 67, # 2 97 | "t": "video", 98 | "tp": 0, 99 | "ts": str(timestap), 100 | "u": int(user_id), 101 | "uip": "", 102 | "v": int(video_id) 103 | } 104 | ) 105 | video_frame += learning_rate 106 | max_time = int((time.time() + 3600) * 1000) 107 | timestap = min(max_time, timestap + 1000 * 15) 108 | data = {"heart_data": heart_data} 109 | r = requests.post(url=url, headers=headers, json=data) 110 | try: 111 | error_msg = json.loads(r.text)["message"] 112 | if "anomaly" in error_msg: 113 | video_frame = 0 114 | except: 115 | pass 116 | try: 117 | delay_time = re.search(r'Expected available in(.+?)second.', r.text).group(1).strip() 118 | print("由于网络阻塞,万恶的雨课堂,要阻塞" + str(delay_time) + "秒") 119 | time.sleep(float(delay_time) + 0.5) 120 | video_frame = 0 121 | print("恢复工作啦~~") 122 | r = requests.post(url=submit_url, headers=headers, data=data) 123 | except: 124 | pass 125 | progress = requests.get(url=get_url, headers=headers) 126 | tmp_rate = re.search(r'"rate":(.+?)[,}]', progress.text) 127 | if tmp_rate is None: 128 | return 0 129 | val = tmp_rate.group(1) 130 | print("学习进度为:" + str(float(val) * 100) + "%/100%" + " last_point: " + str(video_frame)) 131 | time.sleep(0.7) 132 | print("视频" + video_id + " " + video_name + "学习完成!") 133 | return 1 134 | 135 | 136 | def get_videos_ids(course_name, classroom_id, course_sign): 137 | get_homework_ids = "https://changjiang.yuketang.cn/c27/online_courseware/xty/kls/pub_news/29552/" 138 | homework_ids_response = requests.get(url=get_homework_ids, headers=headers) 139 | homework_json = json.loads(homework_ids_response.text) 140 | homework_dic = {} 141 | try: 142 | for i in homework_json["data"]["content_info"]: 143 | if "section_list" in i: 144 | for j in i["section_list"]: 145 | for z in j["leaf_list"]: 146 | if z["leaf_type"] == leaf_type["video"]: 147 | homework_dic[z["id"]] = j["name"] 148 | 149 | else: 150 | print("?????????????") 151 | print(course_name + "共有" + str(len(homework_dic)) + "个作业喔!") 152 | return homework_dic 153 | except: 154 | print("fail while getting homework_ids!!! please re-run this program!") 155 | raise Exception("fail while getting homework_ids!!! please re-run this program!") 156 | 157 | 158 | if __name__ == "__main__": 159 | # your_courses = [] 160 | 161 | # 首先要获取用户的个人ID,即user_id,该值在查询用户的视频进度时需要使用 162 | 163 | # 然后要获取教室id 164 | get_classroom_id = "https://changjiang.yuketang.cn/mooc-api/v1/lms/user/user-courses/?status=1&page=1&no_page=1&term=latest&uv_id=3078" 165 | submit_url = "https://changjiang.yuketang.cn/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id=3078" 166 | classroom_id_response = requests.get(url=get_classroom_id, headers=headers) 167 | ''' 168 | try: 169 | for ins in json.loads(classroom_id_response.text)["data"]["product_list"]: 170 | your_courses.append({ 171 | "course_name": ins["course_name"], 172 | "classroom_id": ins["classroom_id"], 173 | "course_sign": ins["course_sign"], 174 | "sku_id": ins["sku_id"], 175 | "course_id": ins["course_id"] 176 | }) 177 | except Exception as e: 178 | print("fail while getting classroom_id!!! please re-run this program!") 179 | raise Exception("fail while getting classroom_id!!! please re-run this program!") 180 | ''' 181 | 182 | course_sign = "" # 未使用 183 | 184 | homework_dic = get_videos_ids(course_name, classroom_id, course_sign) 185 | 186 | for one_video in homework_dic.items(): 187 | one_video_watcher(one_video[0], one_video[1], course_id, user_id, classroom_id, sku_id) 188 | 189 | ''' 190 | 显示用户提示 191 | for index, value in enumerate(your_courses): 192 | print("编号:"+str(index+1)+" 课名:"+str(value["course_name"])) 193 | number = input("你想刷哪门课呢?请输入编号。输入0表示全部课程都刷一遍\n") 194 | if int(number)==0: 195 | #0 表示全部刷一遍 196 | for ins in your_courses: 197 | homework_dic = get_videos_ids(ins["course_name"],ins["classroom_id"], ins["course_sign"]) 198 | for one_video in homework_dic.items(): 199 | one_video_watcher(one_video[0],one_video[1],ins["course_id"],user_id,ins["classroom_id"],ins["sku_id"]) 200 | else: 201 | #指定序号的课程刷一遍 202 | number = int(number)-1 203 | homework_dic = get_videos_ids(your_courses[number]["course_name"],your_courses[number]["classroom_id"],your_courses[number]["course_sign"]) 204 | for one_video in homework_dic.items(): 205 | one_video_watcher(one_video[0], one_video[1], your_courses[number]["course_id"], user_id, your_courses[number]["classroom_id"], 206 | your_courses[number]["sku_id"]) 207 | ''' 208 | --------------------------------------------------------------------------------