网课邦

当前位置:首页 > 网课资讯

网课资讯

智慧树知到刷课插件(包括直播课)-智慧树视频自动观看脚本

时间:2024-11-30 16:02:47 作者:暂无作者 浏览量:
这个脚本的根本原则是完全的自动化。除了之一次使用需要花些时间配置,后续就只要点击运行即可,登陆、跳转、关闭等等功能都会自动完成。为了达成这个效果,我主要实现了以下功能

这个脚本的根本原则是完全的自动化。除了之一次使用需要花些时间配置,后续就只要点击运行即可,登陆、跳转、关闭等等功能都会自动完成。


为了达成这个效果,我主要实现了以下功能:

基础部分:

1. 首次配置脚本

配置内容包括账号、密码、课程链接和部分Chromedriver configs(静音、无头模式)

之一次配置完成后,输入的信息会被保存在同目录下的zhh_config.txt文件中,后续只要这个文件存在,就不再需要任何输入。如果想更改信息,直接更改txt文件即可。

2. 自动输入账号(手机号)和密码并登录

这个不多说了,模拟登陆都实现不了的话还谈什么刷课。

3. 根据输入的URL自动判断类型

对于输入的URL,脚本会自动判断类型(目前只有Lesson课程和Live直播两种),根据不同的类型调用不同的类进行处理。


针对不同的视频类型,进行以下处理:

课程视频:

1. 自动跳过警告和学前必读

这个智慧树警告我觉得挺有毒的...而且还每次都会跳出来,无语,所以在开始刷课之前必须要检测窗口,然后关掉。

2. 自动点击并关闭视频中跳出的问题界面(答对是不可能的)

手动挂机刷智慧树视频不可行,就在于视频中经常会跳出不计分的问题,且关闭问题之后,视频并不会自动继续播放。

所以脚本就很有必要了,利用脚本,可以实时监控跳出的问题,并点击之一个选项(反正不计分)后关闭,再自动点击播放按钮实现不间断后台刷课。

3. 自动续播

这听起来是一个很蠢的功能,但实际上,智慧树的课程视频播放结束后并不会自动下一节。这大概率也是为了防止挂机有意为之,所以脚本加入这个功能也是必须的。

3. 自动关闭视频中跳出的今日时长提示

我没有具体计时,但这个跳出的时间应该远远大于25分钟,一般出现这个的话就可以关了。但如果想要继续看视频,就仍然需要用脚本把这个提示框关掉,再点击视频播放按钮。

4. 自动定时30分钟关闭

虽然可以在跳出今日时长提示后就关闭,但为了防止时间不够,我还是设置了定时半小时关闭。

5. 在console显示

(1)当前视频标题

(2)实时进度更新

(3)观看时间提示(3分钟1次)

直播视频:

1. 自动关闭提示

无语+1,这些提示为什么每次都要跳出来啊?

2. 跟踪并显示签到进度

在直播页面的下方有一个蓝色的签到进度,显示了这堂直播课已经观看的百分比。这对于下一个功能的实现有极大帮助。

3. 自动前进至签到上次位置

一节直播课时间很长,大概2个多小时,因此很难一次刷完。但和课程视频不同,直播视频每次重新进入都会从00:00:00开始,非常影响效率。

为了解决这个问题,我设置了自动加速,当视频位置在签到位置之前,视频会以每秒2分钟速度加速播放,到了签到位置后则恢复正常速度,因为没有视频,所以只能意会了...这部分也是最复杂的,我想了很多方法,最后找到了一个可以用的 *** 指令。


部分功能实现方法说明:

全说篇幅太长了,稍微挑几个重要的。

1. 实时进度条:

以Live.show_progress()为例:

def show_progress(self):progress=self.dr.find_element_by_xpath('//*[@id="container"]/div[1]/div/div[2]/div[1]/p[2]/span').get_attribute('textContent')print("目前签到进度:"+progress+"%")while True:time.sleep(1)#每间隔1秒检测视频是否播放完try:#该视频的总时间total_time=self.dr.find_element_by_xpath('//*[@id="vjs_forFollowBackDiv"]/div[10]/div[4]/span[2]').get_attribute('textContent')#获取当前播放的进度current_time=self.dr.find_element_by_xpath('//*[@id="vjs_forFollowBackDiv"]/div[10]/div[4]/span[1]').get_attribute('textContent')print("进度:{0}/{1}".format(current_time,total_time), end="\r")if current_time!='00:00:00' and total_time!='00:00:00':if current_time[3:5]==total_time[3:5] and int(current_time[6:])>=(int(total_time[6:])-2):print('\n')print('已完成本次直播',end='\n')self.dr.quit() #退出quit()except:current_time = '00:00'total_time = '00:05' #随意填,两个不同就行

并不复杂,主要就是每隔一秒,找到网页中的 current_time 和 total_time 两个元素提取出文本,然后按格式 print 出来。

唯一需要注意的是 print 最后的 end=’\r’ ,这表示回车,回到某一行的开头,这样可以覆盖前一秒的进度,看起来就像是实时更新了。

2. 直播加速播放:

先看实现的代码:

def play_time(self):while True:time.sleep(0.5)try:progress=self.dr.find_element_by_xpath('//*[@id="container"]/div[1]/div/div[2]/div[1]/p[2]/span').get_attribute('textContent')total_time=self.dr.find_element_by_xpath('//*[@id="vjs_forFollowBackDiv"]/div[10]/div[4]/span[2]').get_attribute('textContent')total_sec=Live.str2sec(total_time)current_time=self.dr.find_element_by_xpath('//*[@id="vjs_forFollowBackDiv"]/div[10]/div[4]/span[1]').get_attribute('textContent')current_sec=Live.str2sec(current_time)sign_sec=int(total_sec*int(progress)/100) #最后签到的时间iterate=int((sign_sec-current_sec)/60)for i in range(iterate):self.dr.execute_script("document.getElementsByTagName('video')[0].currentTime = document.getElementsByTagName('video')[0].currentTime + 60")time.sleep(1)i+=1if i==iterate:print('\n')print('已到达上次观看位置', end='\n')breakexcept:pass

这是一个障眼法,实际并不是加速播放,而是每隔0.5秒将进度条向右拖1分钟,实现方法是这样一个JavaScript:

document.getElementsByTagName('video')[0].currentTime =  document.getElementsByTagName('video')[0].currentTime + 60

只要进行条件判断,符合条件的话就不间断执行这条脚本,看起来就像是加速播放了。

3. 多线程:

上述功能几乎都是并行的,仅靠一个线程难以实现,所以我使用了多线程,这可以从主函数中看出:

def main():configs=config()configs=[i.strip('\n') for i in configs]logins=login(configs)driver=logins[0]class_type=logins[1]threads=[]if class_type=='lesson':Zhihuishu=Lesson(driver)threads.append(threading.Thread(target=Zhihuishu.is_exist))threads.append(threading.Thread(target=Zhihuishu.show_progress))threads.append(threading.Thread(target=Zhihuishu.close_web))threads.append(threading.Thread(target=Zhihuishu.close_timeup))elif class_type=='live':Zhihuishu=Live(driver)threads.append(threading.Thread(target=Zhihuishu.show_progress))threads.append(threading.Thread(target=Zhihuishu.play_time))else:raise Exception('请检查输入的链接,需包含http头')for thr in threads:thr.setDaemon(True)thr.start()for thr in threads:if thr.isAlive: #阻止主线程直接结束thr.join()

首先进行视频类型的判断。

对于课程视频(Lesson类),我定义了五个函数实现功能,自动跳过警告和学前必读的skip_iknow()是更先触发的,因此在__init__()中运行。

而剩下四个是同时进行的,为了防止它们相互干扰,我建立了4个子线程并行处理。直播视频(Live类)也是类似。

对子线程的要求是:每个子线程既可以完整运行(即主线程不会提前结束),又可以在主线程结束后同时结束。

对于之一点,考虑使用Thread.isAlive判断每个子线程是否活跃,如果活跃,则利用Thread.join()将该子线程再次并入,保证主线程始终处于阻塞状态,不会自动终止;

对于第二点,只要将每个子线程都设为守护线程,即可保证当主线程终止时,子线程也全都结束。


一个提问:

身边有很多人也在学Python,所以这里留一个问题,有兴趣的可以想想:

下面这段是Live类中的_init_is_element_present两个方法:

class Live():def __init__(self, dr):self.dr=drself.dr.implicitly_wait(10)time.sleep(2)WebDriverWait(self.dr, 10, 0.5).until_not(EC.presence_of_element_located((By.XPATH,'//*[@id="popbox_title"]')))if not Live.is_element_present(self.dr, By.XPATH, '//*[@id="popbox_title"]'):#首次设置流畅self.dr.execute_script('document.querySelector("#vjs_forFollowBackDiv > div.controlsBar > div.definiBox > div > b.line1bq.switchLine").click()')title=self.dr.find_element_by_xpath('//*[@id="wh_live_name"]').get_attribute('textContent')print("正在播放回放:"+title)def is_element_present(driver, by, value): #判断网页元素是否存在try:element = driver.find_element(by=by, value=value)except NoSuchElementException:return Falsereturn True

其中第7行,进行判断时使用了

Live.is_element_present(self.dr, By.XPATH, '//*[@id="popbox_title"]'):

这个语句,其中调用了is_element_present这个方法。

那么问题来了,

为什么使用的是Live.is_element_present() 而不是 self.is_element_present() 呢?

能回答出来这个问题的话,就说明对类和类方法的理解还是比较深入的。


最后:

这个脚本使用的第三方库只有 selenium 和 webdriver_manager 两个

from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.common.exceptions import NoSuchElementExceptionfrom webdriver_manager.chrome import ChromeDriverManagerimport time, threading, re, os

webdriver_manager 的用处是自动配置 ChromeDriver 简化 *** 作。如果有人需要这个脚本的话,可以找我。

电脑里有 Python 3.8+环境和 Chrome,再安装这两个库应该就可以跑了,当然肯定还有Bug。

至于源码...太长这次就不发了,反正发了也没人看。