用python+selenium编写一个智慧树刷课脚本2020最新版

在智慧树上进行刷课的过程比较繁琐,所以想编写一个刷课脚本,需要解决的问题是将出现的各种弹窗关掉,遇到莫名其妙的暂停时单击自动播放,并且播放完成自动跳转,筛选掉已完成的视频等。解决这些问题就可以将任务放到后台,提高刷课效率。

用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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章