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