在智慧树上进行刷课的过程比较繁琐,所以想编写一个刷课脚本,需要解决的问题是将出现的各种弹窗关掉,遇到莫名其妙的暂停时单击自动播放,并且播放完成自动跳转,筛选掉已完成的视频等。解决这些问题就可以将任务放到后台,提高刷课效率。
用selenium库刷课并不是个最新想法,在此之前已经有很多案例,但是别人出现的各种问题我并没有遇到,这就是个奇怪的地方。
我用的是脚本神器–火狐,官方下载地址,我使用的版本是77.0.1 (64 位),理论上谷歌浏览器也支持,但我在测试过程中总会遇到各种问题。
直接使用手机号和密码登陆,没有绑定手机号到官网个人中心绑定手机号。
在代码中有详细的注释,在这里就不过多解释。
一个课程里还有各种课后题以及作业考试,即使有答案也要一个个查找,非常繁琐,下节我会配合使用题库API接口编写一个刷题脚本,有兴趣的可以关注。
代码直接复制过来即可使用。
import re
import time
import logging
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException, TimeoutException, WebDriverException
#创建一个日志器,方便后期调试
logging.basicConfig(level=logging.INFO, format='%(asctime)s-%(processName)s-%(funcName)s-%(message)s')
logger = logging.getLogger(__name__)
class Video_Player():
def __init__(self):
self.create_browser()#第一步打开浏览器
def create_browser(self):
'''创建一个浏览器对象'''
logger.info('启动elenium.Driver.Firefox')
#打开网页
try:
self.browser = webdriver.Firefox()#先用火狐浏览器打开
except:
self.browser = webdriver.Chrome()#火狐浏览器打开失败换谷歌浏览器
url = 'https://onlineh5.zhihuishu.com/onlineWeb.html#/studentIndex'
self.browser.get(url)
self.wait = WebDriverWait(self.browser, 10)
logger.info('打开网页{0}'.format(url))
def find_course(self):
'''通过课程名搜索课程'''
wait = WebDriverWait(self.browser, 7)
#手动输入要打开的课程
coursename = input('请输入课程名:')
try:
#获得共享课程列表
class_list = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@id="sharingClassed"]//div[contains(@class, "hoverList")]')))
except TimeoutException:
#如果超时没有刷新到课程列表则说明登陆失败
logger.info('密码错误,登陆失败')
self.browser.close()
exit()
#在课程列表中寻找需要打开的课程
for a_class in class_list:
class_name = a_class.find_element_by_css_selector('.courseName').text
if coursename in class_name:
logger.info('已搜索到课程{0}'.format(class_name))
the_class = a_class.find_element_by_css_selector('dd')
#搜索到课程返回节点对象和课程名称
return (the_class, class_name)
#未搜索到返回空
return None
def video_finish(self):
'''视频完成处理事件'''
try:
#寻找视频完成标识
finish = self.playing_video.find_element_by_css_selector('.time_icofinish')
if finish:
logger.info('视频完成处理事件,找到节点.time_icofinish')
#从生成器中获得下一个播放视频进入
self.playing_video = self.get_videos.__next__()
self.playing_video.click()
except NoSuchElementException:
return False
def video_filter(self, videos):
'''筛选掉所有观看完成视频'''
new_video = []
for video in videos:
try:
#视频已完成直接跳过
finish = video.find_element_by_css_selector('.time_icofinish')
except Exception:
#未完成的视频放到未完成的列表中
new_video.append(video)
return new_video
def dialog_test(self):
'''弹框题目处理事件'''
try:
#寻找题目弹窗节点,未找到抛出错误,直接跳过
dialog_text = self.browser.find_element_by_xpath('//div[contains(@class, "dialog-test")]')
#获得所有选项
items = dialog_text.find_elements_by_css_selector('.topic-item')
logger.info('弹框题目处理事件,找到节点.dialog-test')
#将所有选项勾选
for i in range(len(items)):
item = items[i]
try:
#已经勾选的直接跳过
span = item.find_element_by_css_selector('.active')
except NoSuchElementException:
#未勾选的单击勾选
item.click()
logger.info('已勾选')
#获取题目答案
test_answer = dialog_text.find_element_by_css_selector('.answer span')
answer = test_answer.text
logger.info('获得答案{0}'.format(answer))
search_str = r'[ABCDEFG]'
#匹配到所有答案列表
search_list = re.findall(search_str, answer)
#勾选所有的正确答案
for ans in search_list:
index = ord(ans) - ord('A')
item = items[index]
try:
#已经勾选的直接跳过
span = item.find_element_by_css_selector('.active')
except :
#未勾选的单击勾选
item.click()
logger.info('勾选答案{0}'.format(ans))
#单击关闭题目弹窗
btn = dialog_text.find_element_by_css_selector('.btn')
btn.click()
logger.info('找到节点#vjs_container, 关闭弹窗')
return True
except Exception:
return False
def dialog_time(self):
'''超前学习弹窗处理事件'''
try:
#获得超前学习弹窗事件
dialog = self.browser.find_element_by_xpath('//div[contains(@class, "dialog-look-habit")]')
#获得关闭弹窗按键
btn = dialog.find_element_by_css_selector('.el-dialog__headerbtn')
#单击关闭
btn.click()
logger.info('超前学习弹窗处理事件,找到节点.dialog-look-habit,关闭弹窗')
return True
except Exception:
#未找到直接跳过
return False
def dialog_warning(self):
'''学习警告弹窗事件'''
try:
#获得学习警告弹窗事件并获得关闭按键
warning = self.browser.find_element_by_xpath('//div[contains(@class, "dialog-warn")]//button[@class="el-dialog__headerbtn"]')
#单击关闭
warning.click()
logger.info('学习警告处理事件,找到节点.dialog-warn')
return True
except Exception:
#未找到直接跳过
pass
def dialog_reading(self):
'''学前必读弹窗事件'''
try:
#获得学习必读弹窗事件
dialog_read = self.browser.find_element_by_xpath('//div[@class="dialog"]//div[@class="dialog-read"]//i[contains(@class, "iconfont")]')
#单击关闭
dialog_read.click()
logger.info('学前必读处理事件,找到节点.dialog')
return True
except Exception:
#未找到跳过
pass
def dialog_handler(self):
'''处理所有的弹窗事件'''
finish = True
if self.dialog_warning(): finish = False
if self.dialog_reading(): finish = False
if self.dialog_test(): finish = False
if self.dialog_time(): finish = False
#完成返回True,否则返回False
return finish
def paused_handler(self):
'''处理视频暂停事件'''
wait = WebDriverWait(self.browser, 1)
try:
point = 'vjs-paused'
#检查视频是否被暂停
video_panel = self.browser.find_element_by_xpath('//div[contains(@class, "{0}")]'.format(point))
if video_panel:
#单击播放
video_panel.click()
logger.info('发现节点{0}, 转换'.format(point))
wait.until(EC.staleness_of(video_panel))
except Exception:
pass
def get_video(self)
'''获得所有播放视频的生成器'''
for video in self.videos:
yield video
def video_init(self):
'''播放视频初始化函数'''
#等待加载完网页所有节点
all_elements = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//body')))
logger.info('已加载所有节点')
#设定单击事件节点
input_id = self.browser.find_element_by_id('lUsername')#账号输入框
input_pwd = self.browser.find_element_by_id('lPassword')#密码输入框
logger.info('设定单击事件节点')
#登陆
enter_btn = self.browser.find_element_by_css_selector('.wall-sub-btn')#登陆按键
input_id.send_keys(input('请输入账号:'))#填入账号
input_pwd.send_keys(input('请输入密码:'))#填入密码
enter_btn.click()
#课程list
(classed, class_name) = self.find_course()
logger.info('登陆成功')
if classed:
#进入课程
classed.click()
logger.info('进入课程{0}'.format(class_name))
#等待加载完所有视频节点
all_elements = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//li[contains(@class, "clearfix") and contains(@class, "video")]')))
logger.info('已加载所有节点')
#弹窗优先级:测试 > 警告 > 学前必读
logger.info('reflash all dialog')
#获得所有视频
all_videos = self.browser.find_elements_by_xpath('//li[contains(@class, "clearfix") and contains(@class, "video")]')
#筛选出所有未完成视频
self.videos = self.video_filter(all_videos)
if len(self.videos) > 0:
self.get_videos = self.get_video()
logger.info('获得所有video, 找到节点')
#等待处理完所有弹窗
while True:
time.sleep(2)
if self.dialog_handler():
break
logger.info('弹窗全部清除')
try:
#取得未播放视频并点击开始
self.playing_video = self.get_videos.__next__()
self.playing_video.click()
except Exception:
pass
else:
logger.info('课程进度已完成!')
exit()
else:
logger.info('未找到课程,请求退出')
self.browser.close()
exit()
if __name__ == '__main__':
player = Video_Player()
player.video_init()
while True:
player.dialog_test()
player.dialog_time()
player.paused_handler()
player.video_finish()