

这个不多说了,模拟登陆都实现不了的话还谈什么刷课。
对于输入的URL,脚本会自动判断类型(目前只有Lesson课程和Live直播两种),根据不同的类型调用不同的类进行处理。



手动挂机刷智慧树视频不可行,就在于视频中经常会跳出不计分的问题,且关闭问题之后,视频并不会自动继续播放。
所以脚本就很有必要了,利用脚本,可以实时监控跳出的问题,并点击第一个选项(反正不计分)后关闭,再自动点击播放按钮实现不间断后台刷课。

这听起来是一个很蠢的功能,但实际上,智慧树的课程视频播放结束后并不会自动下一节。这大概率也是为了防止挂机有意为之,所以脚本加入这个功能也是必须的。
我没有具体计时,但这个跳出的时间应该远远大于25分钟,一般出现这个的话就可以关了。但如果想要继续看视频,就仍然需要用脚本把这个提示框关掉,再点击视频播放按钮。
虽然可以在跳出今日时长提示后就关闭,但为了防止时间不够,我还是设置了定时半小时关闭。
(1)当前视频标题
(2)实时进度更新
(3)观看时间提示(3分钟1次)


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

在直播页面的下方有一个蓝色的签到进度,显示了这堂直播课已经观看的百分比。这对于下一个功能的实现有极大帮助。
一节直播课时间很长,大概2个多小时,因此很难一次刷完。但和课程视频不同,直播视频每次重新进入都会从00:00:00开始,非常影响效率。
为了解决这个问题,我设置了自动加速,当视频位置在签到位置之前,视频会以每秒2分钟速度加速播放,到了签到位置后则恢复正常速度,因为没有视频,所以只能意会了...这部分也是最复杂的,我想了很多方法,最后找到了一个可以用的JS指令。
1. 实时进度条:
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' #随意填,两个不同就行
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+=1
if i==iterate:
print('n')
print('已到达上次观看位置', end='n')
break
except:
pass
document.getElementsByTagName('video')[0].currentTime =
document.getElementsByTagName('video')[0].currentTime + 60
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__() 中运行。
一个提问:
class Live():
def __init__(self, dr):
self.dr=dr
self.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 False
return True
Live.is_element_present(self.dr, By.XPATH, '//*[@id="popbox_title"]'):
那么问题来了,
为什么使用的是Live.is_element_present() 而不是 self.is_element_present() 呢?
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from webdriver_manager.chrome import ChromeDriverManager
import time, threading, re, os