From acc4b6bdc924fb532135d0520ddbad5a0cc45129 Mon Sep 17 00:00:00 2001 From: Huffer342-WSH <718007138@qq.com> Date: Tue, 2 Apr 2024 16:43:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[modify]:=E5=B0=8F=E4=BC=98=E5=8C=96=201.?= =?UTF-8?q?=E6=8A=8Auniversity=5Fid=E4=BB=A5=E5=8F=8A=E5=AD=A6=E6=A0=A1?= =?UTF-8?q?=E9=9B=A8=E8=AF=BE=E5=A0=82=E7=BD=91=E5=9D=80=E5=88=86=E7=A6=BB?= =?UTF-8?q?=E5=88=B0=E4=BA=86=E6=96=87=E4=BB=B6=E5=BC=80=E5=A4=B4=202.?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=92=AD=E6=94=BE=E9=80=9F=E5=BA=A6?= =?UTF-8?q?=E9=80=89=E9=A1=B9=203.=E6=B7=BB=E5=8A=A0=E4=BA=86=E4=BB=8E?= =?UTF-8?q?=E4=B8=8A=E6=AC=A1=E6=92=AD=E6=94=BE=E7=BB=93=E5=B0=BE=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E6=92=AD=E6=94=BE=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- videoHelper.py | 264 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 192 insertions(+), 72 deletions(-) diff --git a/videoHelper.py b/videoHelper.py index a275aef..351c43a 100644 --- a/videoHelper.py +++ b/videoHelper.py @@ -8,54 +8,118 @@ # 以下的csrftoken和sessionid需要改成自己登录后的cookie中对应的字段!!!!而且脚本需在登录雨课堂状态下使用 # 登录上雨课堂,然后按F12-->选Application-->找到雨课堂的cookies,寻找csrftoken和sessionid字段,并复制到下面两行即可 -csrftoken = "yours" #需改成自己的 -sessionid = "yours" #需改成自己的 +csrftoken = "" # 需改成自己的 +sessionid = "" # 需改成自己的 +university_id = "1234" +yuketang_url = "xxxxx.yuketang.cn" +speed = 2 # 以下字段不用改,下面的代码也不用改动 user_id = "" headers = { - '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', - 'Content-Type': 'application/json', - 'Cookie': 'csrftoken=' + csrftoken + '; sessionid=' + sessionid + '; university_id=3078; platform_id=3', - 'x-csrftoken': csrftoken, - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-origin', - 'university-id': '3078', - 'xtbz': 'cloud' + "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", + "Content-Type": "application/json", + "Cookie": "csrftoken=" + + csrftoken + + "; sessionid=" + + sessionid + + f"; university_id={university_id}; platform_id=3", + "x-csrftoken": csrftoken, + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "university-id": f"{university_id}", + "xtbz": "cloud", } -leaf_type = { - "video": 0, - "homework": 6, - "exam": 5, - "recommend": 3, - "discussion": 4 -} +leaf_type = {"video": 0, "homework": 6, "exam": 5, "recommend": 3, "discussion": 4} + + +def update_progress(progress, total, userStr1="", userStr2=""): + bar_length = 50 + block_width = int(round(bar_length * progress / total)) + status = "[{0}{1}] {2}/{3}".format( + "#" * block_width, "-" * (bar_length - block_width), progress, total + ) + print(userStr1, status, userStr2, end="\r") + + +def one_video_watcher(video_id, video_name, cid, user_id, classroomid, skuid, speed): -def one_video_watcher(video_id,video_name,cid,user_id,classroomid,skuid): video_id = str(video_id) classroomid = str(classroomid) - url = "https://gsscut.yuketang.cn/video-log/heartbeat/" - 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" + url = f"https://{yuketang_url}/video-log/heartbeat/" + get_url = ( + f"https://{yuketang_url}/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) + + f"&snapshot=1&term=latest&uv_id={university_id}" + ) progress = requests.get(url=get_url, headers=headers) - if_completed = '0' + if_completed = "0" try: if_completed = re.search(r'"completed":(.+?),', progress.text).group(1) except: pass - if if_completed == '1': - print(video_name+"已经学习完毕,跳过") + if if_completed == "1": + print(video_name + "已经学习完毕,跳过") return 1 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': + print(video_name + ",尚未学习,现在开始自动学习") + + heart_data = [] + heart_data.append( + { + "i": 5, + "et": "loadeddata", + "p": "web", + "n": "ws", + "lob": "cloud4", + "cp": 0, + "fp": 0, + "tp": 0, + "sp": 1, + "ts": str(int(time.time() * 1000)), + "u": int(user_id), + "uip": "", + "c": cid, + "v": int(video_id), + "skuid": skuid, + "classroomid": classroomid, + "cc": video_id, + "d": 4976.5, + "pg": "4512143_tkqx", + "sq": 2, + "t": "video", + } + ) + data = {"heart_data": heart_data} + requests.post(url=url, headers=headers, json=data) + heart_data = [] + progress = requests.get(url=get_url, headers=headers) + rate_match = re.search(r'"rate":(.+?)[,}]', progress.text) + watch_length_match = re.search(r'"watch_length":(.+?)[,}]', progress.text) + video_length_match = re.search(r'"video_length":(.+?)[,}]', progress.text) + if rate_match is None or watch_length_match is None: + return 0 + val = float(rate_match.group(1)) + watch_length = int(float(watch_length_match.group(1))) + video_length = int(float(video_length_match.group(1))) + print( + f"学习进度:{(val*100):.2f}%,以观看/总量(frame): {watch_length}/{video_length}" + ) + start_frame = watch_length + video_frame = watch_length + time_start = int(time.time() * 1000) + timestap = time_start + + while val < 1: heart_data = [] for i in range(50): heart_data.append( @@ -80,15 +144,17 @@ def one_video_watcher(video_id,video_name,cid,user_id,classroomid,skuid): "d": 4976.5, "pg": "4512143_tkqx", "sq": 2, - "t": "video" + "t": "video", } ) - video_frame += learning_rate - max_time = int((time.time() + 3600) * 1000) - timestap = min(max_time, timestap+1000*15) + time_now = int(time.time() * 1000) + video_frame = int((time_now - time_start) * speed / 1000) + start_frame + video_frame = min(video_frame, video_length) + # print("video_frame", video_frame) + timestap = time_now data = {"heart_data": heart_data} - r = requests.post(url=url,headers=headers,json=data) - print(r.text) + r = requests.post(url=url, headers=headers, json=data) + # print("Respone:", r.text) try: error_msg = json.loads(r.text)["message"] if "anomaly" in error_msg: @@ -96,7 +162,9 @@ def one_video_watcher(video_id,video_name,cid,user_id,classroomid,skuid): except: pass try: - delay_time = re.search(r'Expected available in(.+?)second.', r.text).group(1).strip() + 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 @@ -104,18 +172,40 @@ def one_video_watcher(video_id,video_name,cid,user_id,classroomid,skuid): 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: + progress = requests.get(url=get_url, headers=headers) + # '{"message":null,"code":0,"data":{"45413299":{"first_point":1712042201.856,"last_point":9.0,"completed":0,"watch_length":635,"ult":1712042729.461,"rate":0.4785,"video_length":1327.0}},"45413299":{"first_point":1712042201.856,"last_point":9.0,"completed":0,"watch_length":635,"ult":1712042729.461,"rate":0.4785,"video_length":1327.0}}' + rate_match = re.search(r'"rate":(.+?)[,}]', progress.text) + watch_length_match = re.search(r'"watch_length":(.+?)[,}]', progress.text) + if rate_match is None or watch_length_match is None: return 0 - val = tmp_rate.group(1) - print("学习进度为:" + str(float(val)*100) + "%/100%" + " last_point: " + str(video_frame)) + val = float(rate_match.group(1)) + watch_length = int(float(watch_length_match.group(1))) + # print( + # "学习进度为:" + # + str(val * 100) + # + "%/100%" + # + " last_frame: " + # + str(video_frame) + # + " watch_length:" + # + str(watch_length) + # ) + update_progress(video_frame, video_length, userStr1=video_name) + # 跳转到以观看进度 + if video_frame < watch_length: + start_frame = watch_length + time_start = int(time.time() * 1000) time.sleep(0.7) - print("视频"+video_id+" "+video_name+"学习完成!") + print("视频" + video_id + " " + video_name + "学习完成!") return 1 -def get_videos_ids(course_name,classroom_id,course_sign): - 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 + +def get_videos_ids(course_name, classroom_id, course_sign): + get_homework_ids = ( + f"https://{yuketang_url}/mooc-api/v1/lms/learn/course/chapter?cid=" + + str(classroom_id) + + f"&term=latest&uv_id={university_id}&sign=" + + course_sign + ) homework_ids_response = requests.get(url=get_homework_ids, headers=headers) homework_json = json.loads(homework_ids_response.text) homework_dic = {} @@ -124,61 +214,91 @@ def get_videos_ids(course_name,classroom_id,course_sign): for j in i["section_leaf_list"]: if "leaf_list" in j: for z in j["leaf_list"]: - if z['leaf_type'] == leaf_type["video"]: + if z["leaf_type"] == leaf_type["video"]: homework_dic[z["id"]] = z["name"] else: - if j['leaf_type'] == leaf_type["video"]: + if j["leaf_type"] == leaf_type["video"]: # homework_ids.append(j["id"]) homework_dic[j["id"]] = j["name"] - print(course_name+"共有"+str(len(homework_dic))+"个作业喔!") + 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!") + raise Exception( + "fail while getting homework_ids!!! please re-run this program!" + ) + if __name__ == "__main__": your_courses = [] # 首先要获取用户的个人ID,即user_id,该值在查询用户的视频进度时需要使用 - user_id_url = "https://gsscut.yuketang.cn/edu_admin/check_user_session/" + user_id_url = f"https://{yuketang_url}/edu_admin/check_user_session/" id_response = requests.get(url=user_id_url, headers=headers) try: user_id = re.search(r'"user_id":(.+?)}', id_response.text).group(1).strip() except: print("也许是网路问题,获取不了user_id,请试着重新运行") - raise Exception("也许是网路问题,获取不了user_id,请试着重新运行!!! please re-run this program!") + raise Exception( + "也许是网路问题,获取不了user_id,请试着重新运行!!! please re-run this program!" + ) # 然后要获取教室id - 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" - submit_url = "https://gsscut.yuketang.cn/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id=3078" + get_classroom_id = f"https://{yuketang_url}/mooc-api/v1/lms/user/user-courses/?status=1&page=1&no_page=1&term=latest&uv_id={university_id}" + submit_url = f"https://{yuketang_url}/mooc-api/v1/lms/exercise/problem_apply/?term=latest&uv_id={university_id}" classroom_id_response = requests.get(url=get_classroom_id, headers=headers) try: for ins in json.loads(classroom_id_response.text)["data"]["product_list"]: - your_courses.append({ - "course_name": ins["course_name"], - "classroom_id": ins["classroom_id"], - "course_sign": ins["course_sign"], - "sku_id": ins["sku_id"], - "course_id": ins["course_id"] - }) + your_courses.append( + { + "course_name": ins["course_name"], + "classroom_id": ins["classroom_id"], + "course_sign": ins["course_sign"], + "sku_id": ins["sku_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!") + 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"])) + print("编号:" + str(index + 1) + " 课名:" + str(value["course_name"])) number = input("你想刷哪门课呢?请输入编号。输入0表示全部课程都刷一遍\n") - if int(number)==0: - #0 表示全部刷一遍 + if int(number) == 0: + # 0 表示全部刷一遍 for ins in your_courses: - homework_dic = get_videos_ids(ins["course_name"],ins["classroom_id"], ins["course_sign"]) + 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],one_video[1],ins["course_id"],user_id,ins["classroom_id"],ins["sku_id"]) + one_video_watcher( + one_video[0], + one_video[1], + ins["course_id"], + user_id, + ins["classroom_id"], + ins["sku_id"], + speed, + ) 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"]) + # 指定序号的课程刷一遍 + 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], one_video[1], your_courses[number]["course_id"], user_id, your_courses[number]["classroom_id"], - your_courses[number]["sku_id"]) + one_video_watcher( + one_video[0], + one_video[1], + your_courses[number]["course_id"], + user_id, + your_courses[number]["classroom_id"], + your_courses[number]["sku_id"], + speed, + ) From 55181035620ddfd75026e33cdb785190b1ada914 Mon Sep 17 00:00:00 2001 From: Huffer342-WSH <718007138@qq.com> Date: Tue, 2 Apr 2024 16:45:44 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[new&rename]:=E6=96=87=E6=A1=A3=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 80 +++-------------------------------------------------- original.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 76 deletions(-) create mode 100644 original.md diff --git a/README.md b/README.md index 67ef032..e582fa6 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,7 @@ # yuketangHelper ## 研究生雨课堂网课脚本代码 -homeworkHelper.py是雨课堂网课作业刷题自动脚本 -SingleVideoHelper.py是长江雨课堂版的视频观看脚本 -videoHelper.py是雨课堂网课视频观看自动脚本 -我们雨课堂的网站是:https://gsscut.yuketang.cn/pro/portal/home/ -最后更新日期:2023年2月20日(有同学反馈说脚本还是能用~) -**截止至21年01月18日,刷题bug被修复了,homeworkHelper.py已经作废** +该脚本fork于[yuketangHelper@heyblackC](https://github.com/heyblackC/yuketangHelper) -## 须知 -- 脚本假定使用者具有基本的计算机知识,懂得cookie,会按F12进入开发者模式,且会运行Python代码 -- 两个脚本都是将csrftoken和sessionid更改成自己登录后的cookie中对应的字段后即可运行 -![cookies示例](cookie.png) -- 我知道大佬有很多,请别喷我写的代码,然后也希望一起协同完善呀! - -## 跨校使用 -1. 更改代码中学校的网站地址:https://xxx.yuketang.cn/....(xxx为自己学校的) -2. 根据登录后cookie里的university_id值,更改代码中university-id、university_id、uv_id -感谢@honlu同学的测试 ---- - -## 开发记录 -### videoHelper.py -- v1、v2版本硬编码很多,几乎不能看 -- v3版本能够让用户自行选择刷哪门课 -- v4版本2s一个视频,1分钟十几个视频,项目管理118个视频15min刷完!快到没朋友! -- 它的优点是速度很快,油猴脚本快闪一边,但也因为太快了,视频的观看完成时间会很接近,请自己思考要不要用脚本 - -### SingleVideoHelper.py -- @Sue1347同学提供 -- 长江雨课堂版本,在一些网址上有修改,需要自己填写一些个人信息和课程信息,目前每次只能刷一个课程 - -### homeworkHelper.py -- v1、v2版本甚至无法在别人电脑上运行orz -- v3版本仅能刷项目管理概论作业题,由我独立开发 -- v4版本由素未相识的李同学友情改良,可以刷用户名下所有的课程的线上作业(非常感谢杨同学和他的舍友) -- V5版本旨在跨学院使用,兼容了填空题,另外增加了交互,可以选择想刷哪个课程 -- 寿终正寝啦,已经无法使用 - - - ---- - -## 讲点故事 -> 非技术向 - -最开始产生要写脚本的想法,是在上论文写作课,我看着一堆待完成的视频头疼的要死,于是按出F12查看网页网络流动,用postman去尝试发get或post请求。虽然朋友说雨课堂应该不会有bug的,我还是继续探索了一阵子。无它,就是觉得好玩。被恶作剧一般的心理驱使着,想着要是能写出自动刷视频的脚本并帮助他人,那该有多好。 - -后来发现了通过调整last_point参数可以提高视频的完成率,于是有了最初videoHelper的雏形。磨蹭了几天写出来后,强行让身边的同学在我电脑上登他们的账号帮他们刷完了视频。这个视频的脚本我没敢发到群里,玩了几次后这件事渐渐被我搁置了。 - -12月8日的时候,同学们在班级群讨论项目管理的作业很多(65个),视频也很多(118个)。我也打开了项目管理的作业,看着超出屏幕长度的作业,一股无名怒火冲上脑袋,我按下了F12。一番探索后,我很意外地发现了bug(或者是后门),我招呼自己的舍友都来看了一遍这个好笑bug:后台接口把作业答案也放在json字段中返回了。也就是说,我们只需要把返回的答案再上传到服务器,就能完成作业并拿满分。自然而然地,我基于上述想法开始写自动完成作业的脚本。我是学计算机的,我希望能用编程解放自己的双手。 - -12月9日凌晨,我把最初的v1作业脚本发在了班群里,但第二天我就发现v1脚本换个用户根本运行不了,我有点慌,返回实验室抓紧完善脚本,并请了一位好友帮忙测试,直到好友告诉我脚本很"完美",我才松了一口气,把v3版本的作业脚本发到班群中。直到现在我也不知有没有人被v1版的脚本坑了。如果有,你又恰好在看这段话,那请收下我诚挚的道歉。 - -我想脚本应该是有帮到大家的忙的,知道这一点,我就已经很开心了。但出乎我意料的是,就在9号晚上,杨同学毫无预兆地发出了作业脚本v4,说这是他的舍友李某某改进的。我当时刚夜跑完在走回宿舍,看着消息一路边走边笑:一个根本就不认识的朋友,帮忙改进了代码。我想这就是对代码的热爱吧。李同学有一个很重要的贡献,是发现了leaf_type字段对应视频、作业、考试等不同类型的数据,这是我所忽视掉的东西。我代码写的不好,他花时间看懂了并改进,非常的厉害。 - -然后我把脚本发给在微电子学院的朋友,让他们帮忙测试跨学院通用性。没曾想到他们的专业课中有填空题这种类型,原版没有适配填空题的代码把我好朋友的做题页面搞崩了,服务器提示500,后来经过调查我们发现填空题的答案key应该是answers,而其他的是answer,乱传字段会导致页面崩溃。于是,在12月12日,我将作业代码完善到了v5版本。我同学的壮烈牺牲促成了这一版本的出现(x),哈哈开个玩笑,后来我带我同学去实验室,一顿操作把服务器500给修复好了。 - -再后面不断有知情人找我拿刷视频脚本,于是我也在一下一下的"催更"中基本完善了刷视频脚本videoHelper的v3,我很感谢这些找我拿脚本的朋友。说真的,有人用我写的脚本,我就已经很开心了。这一个仓库呢,也只是留作纪念而已,不敢奢望能有多少个star,有几个人协作,能获得多少使用。 - -我在想,也许明天雨课堂就修复好了作业题的bug,那么homeworkHelper直接作废。 - -我在想,也许雨课堂明天就加强了异常访问检测,把爬虫行为检测出来封禁掉,那么videoHelper直接无效。 - -但起码在此时此刻,它们是有用且有帮助的。 - -2020.12.15 于实验室 - ---- - -2020.12.18 更新 - -今天突然想到可以把heart_data字段中的内容多个合并为单个json数据一齐发出去,这样就能大大提高刷课速度了!于是我根据这个idea开发了v5的视频脚本。现在没有什么视频是1个请求搞不定的(如果有,就2个请求)哈哈哈。 - ---- - -2021.01.18 更新 - -貌似yuketang的接口不再直接返回答案了,好日子过不久呀,homeworkHelper.py作废了。这个仓库算是我第一个真正意义上对他人有用的开源项目,以此留作纪念吧。最初我也没想过会让这个idea一步步从个人使用走到班级使用,再到跨学院使用,最后到跨校使用。不管怎样,这是很好玩的一次经历。 +- 原版本还有学校网址和university_id两个参数需要替换文本,现在将这两个参数和sessionid一样提取出来了,方便跨校使用。 +- 原版本播放速度非常的快,现在新增了播放速度控制。 +- 原版本看视频从头开始看,新增了从上次的记录开始看。 diff --git a/original.md b/original.md new file mode 100644 index 0000000..67ef032 --- /dev/null +++ b/original.md @@ -0,0 +1,79 @@ +# yuketangHelper +## 研究生雨课堂网课脚本代码 +homeworkHelper.py是雨课堂网课作业刷题自动脚本 +SingleVideoHelper.py是长江雨课堂版的视频观看脚本 +videoHelper.py是雨课堂网课视频观看自动脚本 +我们雨课堂的网站是:https://gsscut.yuketang.cn/pro/portal/home/ +最后更新日期:2023年2月20日(有同学反馈说脚本还是能用~) +**截止至21年01月18日,刷题bug被修复了,homeworkHelper.py已经作废** + +## 须知 +- 脚本假定使用者具有基本的计算机知识,懂得cookie,会按F12进入开发者模式,且会运行Python代码 +- 两个脚本都是将csrftoken和sessionid更改成自己登录后的cookie中对应的字段后即可运行 +![cookies示例](cookie.png) +- 我知道大佬有很多,请别喷我写的代码,然后也希望一起协同完善呀! + +## 跨校使用 +1. 更改代码中学校的网站地址:https://xxx.yuketang.cn/....(xxx为自己学校的) +2. 根据登录后cookie里的university_id值,更改代码中university-id、university_id、uv_id +感谢@honlu同学的测试 +--- + +## 开发记录 +### videoHelper.py +- v1、v2版本硬编码很多,几乎不能看 +- v3版本能够让用户自行选择刷哪门课 +- v4版本2s一个视频,1分钟十几个视频,项目管理118个视频15min刷完!快到没朋友! +- 它的优点是速度很快,油猴脚本快闪一边,但也因为太快了,视频的观看完成时间会很接近,请自己思考要不要用脚本 + +### SingleVideoHelper.py +- @Sue1347同学提供 +- 长江雨课堂版本,在一些网址上有修改,需要自己填写一些个人信息和课程信息,目前每次只能刷一个课程 + +### homeworkHelper.py +- v1、v2版本甚至无法在别人电脑上运行orz +- v3版本仅能刷项目管理概论作业题,由我独立开发 +- v4版本由素未相识的李同学友情改良,可以刷用户名下所有的课程的线上作业(非常感谢杨同学和他的舍友) +- V5版本旨在跨学院使用,兼容了填空题,另外增加了交互,可以选择想刷哪个课程 +- 寿终正寝啦,已经无法使用 + + + +--- + +## 讲点故事 +> 非技术向 + +最开始产生要写脚本的想法,是在上论文写作课,我看着一堆待完成的视频头疼的要死,于是按出F12查看网页网络流动,用postman去尝试发get或post请求。虽然朋友说雨课堂应该不会有bug的,我还是继续探索了一阵子。无它,就是觉得好玩。被恶作剧一般的心理驱使着,想着要是能写出自动刷视频的脚本并帮助他人,那该有多好。 + +后来发现了通过调整last_point参数可以提高视频的完成率,于是有了最初videoHelper的雏形。磨蹭了几天写出来后,强行让身边的同学在我电脑上登他们的账号帮他们刷完了视频。这个视频的脚本我没敢发到群里,玩了几次后这件事渐渐被我搁置了。 + +12月8日的时候,同学们在班级群讨论项目管理的作业很多(65个),视频也很多(118个)。我也打开了项目管理的作业,看着超出屏幕长度的作业,一股无名怒火冲上脑袋,我按下了F12。一番探索后,我很意外地发现了bug(或者是后门),我招呼自己的舍友都来看了一遍这个好笑bug:后台接口把作业答案也放在json字段中返回了。也就是说,我们只需要把返回的答案再上传到服务器,就能完成作业并拿满分。自然而然地,我基于上述想法开始写自动完成作业的脚本。我是学计算机的,我希望能用编程解放自己的双手。 + +12月9日凌晨,我把最初的v1作业脚本发在了班群里,但第二天我就发现v1脚本换个用户根本运行不了,我有点慌,返回实验室抓紧完善脚本,并请了一位好友帮忙测试,直到好友告诉我脚本很"完美",我才松了一口气,把v3版本的作业脚本发到班群中。直到现在我也不知有没有人被v1版的脚本坑了。如果有,你又恰好在看这段话,那请收下我诚挚的道歉。 + +我想脚本应该是有帮到大家的忙的,知道这一点,我就已经很开心了。但出乎我意料的是,就在9号晚上,杨同学毫无预兆地发出了作业脚本v4,说这是他的舍友李某某改进的。我当时刚夜跑完在走回宿舍,看着消息一路边走边笑:一个根本就不认识的朋友,帮忙改进了代码。我想这就是对代码的热爱吧。李同学有一个很重要的贡献,是发现了leaf_type字段对应视频、作业、考试等不同类型的数据,这是我所忽视掉的东西。我代码写的不好,他花时间看懂了并改进,非常的厉害。 + +然后我把脚本发给在微电子学院的朋友,让他们帮忙测试跨学院通用性。没曾想到他们的专业课中有填空题这种类型,原版没有适配填空题的代码把我好朋友的做题页面搞崩了,服务器提示500,后来经过调查我们发现填空题的答案key应该是answers,而其他的是answer,乱传字段会导致页面崩溃。于是,在12月12日,我将作业代码完善到了v5版本。我同学的壮烈牺牲促成了这一版本的出现(x),哈哈开个玩笑,后来我带我同学去实验室,一顿操作把服务器500给修复好了。 + +再后面不断有知情人找我拿刷视频脚本,于是我也在一下一下的"催更"中基本完善了刷视频脚本videoHelper的v3,我很感谢这些找我拿脚本的朋友。说真的,有人用我写的脚本,我就已经很开心了。这一个仓库呢,也只是留作纪念而已,不敢奢望能有多少个star,有几个人协作,能获得多少使用。 + +我在想,也许明天雨课堂就修复好了作业题的bug,那么homeworkHelper直接作废。 + +我在想,也许雨课堂明天就加强了异常访问检测,把爬虫行为检测出来封禁掉,那么videoHelper直接无效。 + +但起码在此时此刻,它们是有用且有帮助的。 + +2020.12.15 于实验室 + +--- + +2020.12.18 更新 + +今天突然想到可以把heart_data字段中的内容多个合并为单个json数据一齐发出去,这样就能大大提高刷课速度了!于是我根据这个idea开发了v5的视频脚本。现在没有什么视频是1个请求搞不定的(如果有,就2个请求)哈哈哈。 + +--- + +2021.01.18 更新 + +貌似yuketang的接口不再直接返回答案了,好日子过不久呀,homeworkHelper.py作废了。这个仓库算是我第一个真正意义上对他人有用的开源项目,以此留作纪念吧。最初我也没想过会让这个idea一步步从个人使用走到班级使用,再到跨学院使用,最后到跨校使用。不管怎样,这是很好玩的一次经历。