在智慧樹上進行刷課的過程比較繁瑣,所以想編寫一個刷課腳本,需要解決的問題是將出現的各種彈窗關掉,遇到莫名其妙的暫停時單擊自動播放,並且播放完成自動跳轉,篩選掉已完成的視頻等。解決這些問題就可以將任務放到後臺,提高刷課效率。
用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()