前言
再次臨近升學考試,網課學習占總成績的30%,需要耗費大量的時間精力來掛課,故編寫該腳本來平替人工刷課,如果你也是刷的青島理工大學繼續教育學院的課程,可以直接拿來使用,如果用不著,就當看個樂子,我也只當做留存,以后有代碼可以復用的地方可以再回來參考。
運行環境
- Win 10
- Chrome 瀏覽器 版本 125.0.6422.142(正式版本) (64 位)
- Python 3.12.3
上面只是我本地開發時用到的環境,沒有做太多的適配,理論上只要版本不要過低應該問題不大,不需要刻意指定瀏覽器和python版本(3.7+)
運行效果


代碼
下面的代碼沒有太高的技術難度可言,細節部分沒有過多的優化,不想浪費太多時間在這上面
import time
from datetime import datetime
from selenium import webdriver
from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
def start():
# 設置Chrome選項
chrome_options = Options()
# 設置不自動關閉瀏覽器
chrome_options.add_experimental_option('detach', True)
# 實例化Chrome驅動并設置選項
driver = webdriver.Chrome(options=chrome_options)
# 打開網頁并執行操作...
driver.get('http://cj.qutjxjy.cn/login')
# 校驗頁面是否跳轉
checkPageHasJump(driver)
# 開始刷課
goToPlay(driver)
print("開始刷課")
while True:
currentTab = getCurrentTab(driver)
if currentTab.get_attribute("id") == "dct3":
driver.execute_script("arguments[0].click();", getNextBtn(driver))
# 判斷視頻是否暫停播放
if (currentTab.get_attribute("id") == "dct1") & ("vjs-paused" in getVideoDivClass(driver)):
print("當前視頻暫停播放")
# 切換到頂部DOM
driver.switch_to.default_content()
# 定位到有video的iframe
driver.switch_to.frame(
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "iframe"))))
print("切換到id=iframe的iframe")
# 繼續鉆入
driver.switch_to.frame(0)
print("切換到id=iframe的子iframe")
time.sleep(5)
# 執行JavaScript代碼,點擊播放按鈕,并以2.0倍速播放
print("嘗試通過js腳本播放視頻")
driver.execute_script("""
var video = $(document).find("video")[0]
if (!!video && video.playbackRate) {
if (!video.paused) { // 如果視頻當前沒有暫停,則先暫停再播放
video.pause();
video.muted = true;//靜音
}
video.playbackRate = 2.0; // 設置播放速度為2.0倍
video.play(); // 開始播放視頻
}
""")
try:
palyBtn = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".vjs-big-play-button")))
driver.execute_script("arguments[0].click();", palyBtn)
except TimeoutException:
print("沒有找到播放按鈕,")
print("繼續播放當前視頻")
# 校驗當前視頻是否播放完成
if checkVideoIsPalyCompleted(driver):
print("當前視頻播放完成")
# 判斷視頻是否全部播放完成
if checkAllVideoIsComplete(driver):
print("當前課程所有視頻都已播放完成,準備回到首頁,切換別的課程學習")
# 關閉當前窗口
driver.close()
print("關閉當前窗口")
# 切換到最近的窗口
driver.switch_to.window(driver.window_handles[-1])
print("回到首頁")
# 刷新當前頁面
driver.refresh()
print("刷新首頁")
# 開始刷課
goToPlay(driver)
print("開始刷課")
# 獲取【下一節】按鈕并點擊
driver.execute_script("arguments[0].click();", getNextBtn(driver))
print("點擊【下一節】按鈕")
# 視頻未播放完成
else:
print(datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ":當前視頻暫未播放完成,繼續等待...")
time.sleep(5)
# 如果有彈窗,則點擊彈窗里的【去學習】按鈕
isOpenModal(driver)
def getCurrentTab(driver):
driver.switch_to.default_content()
return WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'span.currents')))
def isOpenModal(driver):
# 切換到頂部DOM
driver.switch_to.default_content()
print("切換到頂部DOM")
try:
modal = driver.find_element(By.ID, "jobFinishTip")
except NoSuchElementException:
print("沒找到彈窗")
return
display_style = modal.value_of_css_property("display")
isOpen = display_style != "none"
print("當前是否彈窗:" + ("是" if isOpen else "否"))
if isOpen:
# 嘗試查找單個匹配的'a.nextbutton'元素
next_button = driver.find_element(By.CSS_SELECTOR, "a.nextChapter")
# 如果找到元素,則點擊它
driver.execute_script("arguments[0].click();", next_button)
print("找到了彈窗里的【去學習】按鈕,并點擊了它")
def goToPlay(driver):
# 等待首頁-菜單欄【課程】按鈕加載完成并點擊
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "icon-kc"))).click()
print("點擊【首頁】-【菜單欄】-【課程】按鈕")
# 定位到iframe并切換(由于【進入學習】按鈕在內嵌的iframe中,故需要切換一下iframe】)
driver.switch_to.frame(WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "frame_content"))))
print("切換到id=frame_content的iframe")
# 等待頁面中【進入學習】按鈕加載完成并點擊,點擊后會新開tab頁面,下面需要切換到新開的窗口
getJoinStudyBtn(driver).click()
print("點擊【右側內容區】-【進入學習】按鈕")
# 獲取所有窗口句柄,通常初始頁面的句柄為0,新打開的頁面句柄為1,依此類推
all_windows = driver.window_handles
# 切換到最新打開的窗口,即索引為-1的窗口
driver.switch_to.window(all_windows[-1])
print("跳轉到【進入學習】的新開頁")
assert "-首頁" in driver.title
print("新開頁加載完成")
# 獲取首頁-課程詳情頁面(【進入學習】頁)的,列表中第一個黃色小圓點
em = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".orange")))
# 點擊黃色小圓點上層的a標簽
getA(em).click()
print("點擊課程列表中第一個小黃點的鏈接")
# 查找第一個【進入學習】按鈕
def getJoinStudyBtn(driver):
# 查找所有class為"percent"的div元素
percent_divs = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'div.percent')))
# 遍歷每一個找到的percent_div
for div in percent_divs:
# 在當前div下查找title不包含"100"的span元素
try:
span = div.find_element(By.XPATH, './/span[not(contains(@title, "學習進度:100.0"))]')
# 定位到該span的父級的父級的父級元素
third_parent = span.find_element(By.XPATH, 'ancestor::div[2]')
# 在這個父元素下查找樣式為"study ddsubstyle"的a標簽
return third_parent.find_element(By.CSS_SELECTOR, 'a.study.ddsubstyle')
except NoSuchElementException:
print("未找到符合條件的span元素或a標簽。")
continue
def checkVideoIsPalyCompleted(driver):
# 定位到頂部
driver.switch_to.default_content()
# 定位到有video的iframe
driver.switch_to.frame(WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "iframe"))))
return "ans-job-finished" in WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".ans-attach-ct"))).get_attribute("class").split()
def getVideoDivClass(driver):
# 定位到頂部
driver.switch_to.default_content()
print("切換到頂層DOM")
# 定位到有video的iframe
driver.switch_to.frame(WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "iframe"))))
print("切換到id=iframe的iframe")
# 繼續鉆入
driver.switch_to.frame(0)
print("切換到id=iframe的子iframe")
# 找到視頻div
try:
video = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.ID, "video")))
return video.get_attribute("class").split()
except TimeoutException:
print("當前不是教學視頻tab。")
return ""
def getNextBtn(driver):
driver.switch_to.default_content()
try:
otherVideoBtn = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.XPATH, "http:// *[starts-with(@id, 'dct')][last()]"))
)
driver.execute_script("arguments[0].click();", otherVideoBtn)
print("切換到最后一個tab頁")
except TimeoutException:
print("沒找到其他視頻按鈕,準備【教學視頻】tab下的【下一節】按鈕")
return WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "right1")))
def checkAllVideoIsComplete(driver):
driver.switch_to.default_content()
return len(driver.find_elements(By.CSS_SELECTOR, "span.orange01")) < 1
def getA(em):
# 獲取 .orange 元素的父元素鏈路
parent_elements = em.find_elements(By.XPATH, "../..")
# 遍歷父元素,直到找到一個 <a> 標簽為止
for parent in parent_elements:
if parent.tag_name == 'a':
return parent
# 確保找到了 <a> 標簽的父元素
if 'a' not in locals():
raise Exception("沒有找到<em class='orange'>元素上級的<a>標簽")
def checkPageHasJump(driver):
# 定義等待時間和間隔時間
wait = WebDriverWait(driver, 10) # 最大等待時間為10秒
# 每隔一秒檢查頁面是否跳轉
while True:
try:
# 使用WebDriverWait來檢查頁面標題是否包含特定關鍵字,這里以"example"為例
wait.until(EC.title_contains("青島理工大學繼教學院"))
print("登錄成功,頁面已跳轉...")
break
except:
print("等待用戶完成登錄...")
# 每隔1秒檢查一次
time.sleep(1)
# 按裝訂區域中的綠色按鈕以運行腳本。
if __name__ == '__main__':
start()
# 訪問 https://www.jetbrains.com/help/pycharm/ 獲取 PyCharm 幫助
打包成可執行文件
- 安裝 pyinstaller
pip install pyinstaller
- 打包:
- –onefile: 這是一個選項,它告訴 PyInstaller 將所有的內容(包括你的腳本、依賴的庫等)打包到一個單獨的可執行文件中。如果不使用這個選項,PyInstaller 通常會創建一個包含多個文件和目錄的文件夾。
- –debug=all: 這是一個帶有參數的選項,其中 --debug 表示你想要啟用調試模式,而 all 是這個選項的參數,表示你想要啟用所有的調試信息。這通常用于在打包過程中或運行打包后的程序時獲取更多的調試輸出,以幫助診斷問題。
- .\main.py: 這是你想要打包的 Python 腳本的路徑。. 表示當前目錄,\main.py 是你的腳本文件名。注意在 Unix-like 系統(如 Linux 或 macOS)中,路徑分隔符是 / 而不是 \,但在 Windows 中你可以使用 \ 或 /。
- -p “C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages”: 這是一個選項,用于指定額外的 Python 搜索路徑。-p 或 --paths 選項后面跟著的是路徑列表,這些路徑會被添加到 Python 的 sys.path 中,這樣 PyInstaller 就能在這些路徑下找到你的腳本所依賴的庫。在這個例子中,你指定了 Python 3.11 的 site-packages 目錄作為額外的搜索路徑。
pyinstaller --onefile --debug=all .\main.py -p "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages"
注意事項
此腳本僅供交流學習使用,嚴禁商業用途
切勿隨意點擊頁面
當腳本運行起來后,出了登錄,不建議再有其他操作,比如點擊頁面的各個按鈕,可能會導致腳本操作邏輯混亂
包含測驗題的課程
部分課程可能包含測驗題,這個時候腳本會默認先將所有課程視頻刷完,如果這個時候測驗題沒有答完,腳本是不會繼續刷下一節課的,這里需要注意一下!!
程序調用瀏覽器驅動超時
由于較新的谷歌瀏覽器默認開啟自動釋放內存模式,針對了打開單未使用的網站會自動清理該頁簽的內存,故,如果遇到腳本報錯,打開Chrome瀏覽器按照如下方式配置即可:

腳本更新
關于該網課的網站,就目前來說更新頻率是很低的,至少在我上這兩年網課期間沒有見到過什么大的更新,腳本也很簡單,就算到時候有更新,看看代碼稍作修改即可