网课选修课一键刷视频脚本—选修课一键自动看视频插件
现在很多学校都大量布置了在线课程的设置,需要进行在线网课的学习。但由于网课数量繁多,视频内容冗长,一个个看过去属实不容易,有没有一键刷网课视频的脚本。
1.代码原理以及实现思路
不管是什么样的视频,要想时刻记录当前视频的播放记录,就一定需要每间隔一段时间就向服务器发送一个请求来告诉服务器当前的播放状态,因此只需要通过网络抓包找到请求,通过python代码模拟网页向服务器发送请求即可。
1.1 找到发送视频播放状态的请求接口
通过浏览器F12的开发者工具,在网络请求中可以看到,播放视频的时候,每间隔30s,浏览器就会向服务器发送一个名为heartbeat的请求。从而就可以获取到该post请求的接口URL:https://yjsy-fjnu.yuketang.cn/video-log/heartbeat/
1.2 分析post请求的发送数据
点击开发者工具中的负载,可以看到本地向服务器发送的post请求的json数据。
视频之一条post请求的json数据
视频第二条post请求的json数据
视频最后一条post请求的json数据
经过分析可以得知,之一次的请求会发送大约10个请求数据,其类型包含’heartbeat’,‘pause’,‘play’,‘playing’等等。第二次和之后的请求,只要视频是正常播放的,就会稳定发送6个左右的’heartbeat’。最后一个请求可以看到发送了一个’videoend’类型的请求。通过以上规律可以断言
'heartbeat’是必不可少的,它决定着视频的播放进度,而视频最后的’videoend’似乎也是不可或缺,标志着视频的结束。
1.3 *** ON请求数据分析
有了以上猜测,接下来就需要分析json请求中的数据含义了。展开其中一条json请求,可以看到
{ "i":5, "et":"heartbeat", "p":"web", "n":"ali-cdn.xuetangx.com", "lob":"cloud4", "cp":634.2, "fp":0, "tp":643.7, "sp":2, "ts":"1654782747174", "u":, "uip":"", "c":, "v":, "skuid":, "classroomid":"", "cc":"", "d":643.7, "pg":"", "sq":127, "t":"video", "cards_id":0, "slide":0, "v_url":""}
通过多条json数据的规律可以总结出以下关键内容的含义:
et:用于标识json数据的类型
cp:当前视频进度
tp:视频总长度
ts:当前时间戳
u:用户ID
c:课程ID
v:视频ID
classroomid:教室ID
cc:某种标记吧
pg:视频的标记吧
sq:当前json数据是所发送的json中的第几条
因此上述内容很显然是会变动的,因此在每次请求的时候都需要进行变量的设置。
2.代码编写
由于我已经事先使用postman测试过接口和对应json请求的可用性,因此这里我就不多做可行性分析,直接默认上述方式可用。但实际应用中需要先使用postman进行可行性分析后再进行代码编写,防止做了无用功。
2.1 *** ON数据设置
首先考虑一个视频的情况,我们需要提供给服务器的就是一个json数组。根据前面总结的经验,我们只需要若干个’heartbeat’类型的数据加末尾一个’videoend’类型的数据即可,同时对若干个’heartbeat’使用sq进行区分。使用postman测试,发现可以将cp从0到ed值的json数组一次性发送给服务器,以完成视频的观看,因此在构建json请求的时候可以一次性将需要发送的json请求设置好。
2.2 标头内容设置
头文件的内容可以直接在浏览器开发者工具中的请求中拿到,需要注意的是,里面的cookie是需要根据实际情况进行设置的:
headers = { "Content-Type": "application/json", "accept": "application/json, text/plain, */*", ... ... ... "accept-encoding": "gzip, deflate, br", "xtbz": "cloud"}
2.3 单个视频的函数编写
具体代码如下:
def request(t, u, c, v, skuid, classroomid, cc, headers): heart_data = [] # 生成post请求的json结构体 for i in range(1, 107, 5): init_body = { "i": 5, "et": "heartbeat", "p": "web", "n": "ali-cdn.xuetangx.com", "lob": "cloud4", "cp": int((i - 1) * END_TIME / 106), "fp": 0, "tp": 0, "sp": 2, "ts": f"{t + i * 10000}", "u": u, "uip": "", "c": c, "v": v, "skuid": skuid, "classroomid": f"{classroomid}", "cc": f"{cc}", "d": END_TIME, "pg": f"{v}_13xj6", "sq": i, "t": "video", "cards_id": 0, "slide": 0, "v_url": "" } heart_data.append(init_body) requestBody = { "heart_data": heart_data } json_dumps = json.dumps(requestBody) # 发送post请求 resp = requests.post( url='https://yjsy-fjnu.yuketang.cn/video-log/heartbeat/', headers=headers, data=json_dumps ) print(f'已刷视频{v},刷新后可以查看结果')
2.4 初始化常量与变量的设置
进行一些简单的变量配置,以适应更多用户。
# 变量设置,必须修改u = # 用户的IDc = # 课程的IDcc = # 不懂是啥,但好像也需要skuid = # 不懂是啥,需要修改classroomid = # 好像是什么教室的idcookie = csrftoken = # 这个从cookie里面拿,头文件要用到interal_time = 1 # 间隔多长时间刷一个视频,单位为秒VIDEOS = [] # 要刷的视频列表,从下面的三个VIDEOS_xxx里面选# 初始化常量配置,不建议修改VIDEOS_9453586 = [i for i in range(9844337, 9844400)] # 科研伦理与学术规范 视频编号VIDEOS_9453588 = [9844489, 9844529, 9844530, 9844531, 9844532, 9844533, 9844536, 9844537, 9844538, 9844539] # 生物医药实验室安全知识 视频编号VIDEOS_9453589 = [i for i in range(9844549, 9844679)] # 工程伦理(2.0) 视频编号END_TIME = 1999.0 # 每个视频的结束时间,如果代码执行完有部分视频没有到100%,可以考虑吧这个数值改大一些。
2.5 主程序入口
前面进行了函数的编写和变量的配置,现在只要根据视频列表中的视频编号逐个发送请求即可。由于每个视频之间的请求并没有延时,因此一次性发送大量的请求很容易导致服务器拒绝接收请求,因此必须设置一个sleep来减慢请求发送的频率。具体代码如下:
if __name__ == '__main__': for video in VIDEOS: t = int(time() * 1000) request(t, u, c, video, skuid, classroomid, cc, headers) sleep(1)
3.还需改善的内容
json数据内容还有很多部分是需要进行重新修改的。如果反复发送这样的请求,后端服务器是很容易发现异常的。因此,对于sq、pg、cc等数据的设置还需要更加仔细地研究。但由于本人使用的需求不多,就不做过多的研究了。
待刷视频数组我并没有仔细考究,只是使用一个列表生成了大致的视频所在区间,这么做虽然可以100%覆盖到需要刷的视频,但还有可能覆盖到其他不需要看的视频,有很大的风险。
对于失败的情况并没有进行异常分析,只是暴力地输出刷视频成功的日志,这样代码健壮性并不强,还有待改进。
json数据是一次性发送给服务器的,服务器竟然不做甄别直接认定是看完了视频,说实话这样也是很容易出现异常被服务器检测到的。为了防止出现异常,还是需要像浏览器一样间隔30s向服务器发送一个post请求。
4.程序详细代码附录
import requestsimport jsonfrom time import time, sleep# 变量设置,必须修改u = # 用户的IDc = # 课程的IDcc = # 不懂是啥,但好像也需要skuid = # 不懂是啥,需要修改classroomid = # 好像是什么教室的idcookie = csrftoken = # 这个从cookie里面拿,头文件要用到interal_time = 1 # 间隔多长时间刷一个视频,单位为秒VIDEOS = [] # 要刷的视频列表,从下面的三个VIDEOS_xxx里面选# 初始化常量配置,不建议修改VIDEOS_9453586 = [i for i in range(9844337, 9844400)] # 科研伦理与学术规范 视频编号VIDEOS_9453588 = [9844489, 9844529, 9844530, 9844531, 9844532, 9844533, 9844536, 9844537, 9844538, 9844539] # 生物医药实验室安全知识 视频编号VIDEOS_9453589 = [i for i in range(9844549, 9844679)] # 工程伦理(2.0) 视频编号END_TIME = 1999.0 # 每个视频的结束时间,如果代码执行完有部分视频没有到100%,可以考虑吧这个数值改大一些。headers = { "Content-Type": "application/json", "accept": "application/json, text/plain, */*", ... ... ... "x-csrftoken": csrftoken, "xtbz": "cloud"}# 发送请求的函数def request(t, u, c, v, skuid, classroomid, cc, headers): heart_data = [] # 生成post请求的json结构体 for i in range(1, 107, 5): init_body = { "i": 5, "et": "heartbeat", "p": "web", "n": "ali-cdn.xuetangx.com", "lob": "cloud4", "cp": int((i - 1) * END_TIME / 106), "fp": 0, "tp": 0, "sp": 2, "ts": f"{t + i * 10000}", "u": u, "uip": "", "c": c, "v": v, "skuid": skuid, "classroomid": f"{classroomid}", "cc": f"{cc}", "d": END_TIME, "pg": f"{v}_13xj6", "sq": i, "t": "video", "cards_id": 0, "slide": 0, "v_url": "" } heart_data.append(init_body) requestBody = { "heart_data": heart_data } json_dumps = json.dumps(requestBody) # 发送post请求 resp = requests.post( url='https://yjsy-fjnu.yuketang.cn/video-log/heartbeat/', headers=headers, data=json_dumps ) print(f'已刷视频{v},刷新后可以查看结果')# 主函数入口if __name__ == '__main__': for video in VIDEOS: t = int(time() * 1000) request(t, u, c, video, skuid, classroomid, cc, headers) sleep(1)