1.购买服务器阿里云:服务器购买地址https://t.aliyun.com/U/PfsP97若失效,可用地址
阿里云:
服务器购买地址
https://t.aliyun.com/U/PfsP97
若失效,可用地址
https://www.aliyun.com/activity/wuying/dj?source=5176.29345612&userCode=49hts92d
腾讯云:
https://curl.qcloud.com/wJpWmSfU
若失效,可用地址
https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console
华为云
https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905
2.部署教程
3.代码如下
# -*- coding:utf-8 -*-
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.options import Options
import configparser
# 读取配置文件
config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")
# 获取用户名和密码
USERNAME = config.get("login", "username")
PASSWORD = config.get("login", "password")
# 获取学习课程类型
course_type = config.get("course_type","type")
class AutoCourseBot:
def __init__(self, username, password):
# 配置Chrome选项,启用静音
chrome_options = Options()
chrome_options.add_argument("--mute-audio")
self.driver = webdriver.Chrome(options=chrome_options)
self.driver.maximize_window()
self.wait = WebDriverWait(self.driver, 15)
self.username = username
self.password = password
# ========== 工具函数 ==========
# def handle_popup(self):
# """处理学习过程中的弹窗"""
# try:
# popup_btn = self.driver.find_element(By.CSS_SELECTOR, "div.score-popup button.close-btn")
# if popup_btn.is_displayed():
# popup_btn.click()
# print(" 弹窗已关闭,继续学习")
# time.sleep(3)
# except NoSuchElementException:
# pass
def handle_popup(self):
"""处理学习过程中的弹窗(优化版)"""
try:
# 关键:等待弹窗按钮出现(最多等10秒),确保弹窗已加载
popup_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "div.score-popup button.close-btn"))
)
# 确认按钮可见且可点击
if popup_btn.is_displayed() and popup_btn.is_enabled():
popup_btn.click()
print(" 弹窗已关闭,继续学习")
# 等待弹窗消失(避免后续操作受影响)
WebDriverWait(self.driver, 10).until(
EC.invisibility_of_element_located((By.CSS_SELECTOR, "div.score-popup"))
)
time.sleep(3)
except Exception:
pass
def handle_leave_page_tip(self, wait_time=5):
try:
# 等待弹窗容器出现,最多等待wait_time秒
modal_container = WebDriverWait(self.driver, wait_time).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.leave-page-tip-modal-container"))
)
# 确认弹窗可见
if modal_container.is_displayed():
print("检测到学习状态中断提醒弹窗")
# 查找并点击"继续学习"按钮
continue_button = modal_container.find_element(
By.CSS_SELECTOR, "button.button"
)
continue_button.click()
print("已点击继续学习按钮")
# 等待弹窗消失
WebDriverWait(self.driver, wait_time).until(
EC.invisibility_of_element_located((By.CSS_SELECTOR, "div.leave-page-tip-modal-container"))
)
except TimeoutException:
pass
# except Exception as e:
# print(f"处理弹窗时发生错误: {str(e)}")
try:
play_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "xg-play .xgplayer-icon-play"))
)
play_btn.click()
except NoSuchElementException:
print(f"出现中断弹窗后,点击播放时出现异常!")
def click_ai_option(self):
try:
# 此try中的代码为规避系统检测
# 找出AI题目标签
quiz_main = self.driver.find_element(By.CSS_SELECTOR,
"div.quiz-main")
time.sleep(2)
if quiz_main:
# 获取所有选项标签
options = quiz_main.find_elements(By.TAG_NAME, "li")
# 随机选择一个选项
random_option = random.choice(options)
# 点击选项中的单选按钮(quiz-radio元素)
# 优先点击单选按钮区域,更符合实际交互逻辑
# radio_button = random_option.find_element(By.CLASS_NAME, "quiz-radio")
random_option.click()
except NoSuchElementException:
pass
def set_playback_rate_to_2x(self, wait_time=5):
try:
# 等待播放速度控制元素出现
playbackrate_element = WebDriverWait(self.driver, wait_time).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "xg-playbackrate.xgplayer-playbackrate"))
)
# 点击播放速度控制元素,展开速度选择列表
playbackrate_element.click()
print("已点击播放速度控制器,展开选择列表")
# 查找并点击2倍速选项(cname="2"的li元素)
two_x_element = WebDriverWait(self.driver, wait_time).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "xg-playbackrate.xgplayer-playbackrate li[cname='2']"))
)
two_x_element.click()
print("已设置播放速度为2倍速")
except TimeoutException:
print("超时:未找到播放速度控制元素或2倍速选项")
except NoSuchElementException:
print("未找到播放速度控制元素或2倍速选项")
except Exception as e:
print(f"设置播放速度时发生错误: {str(e)}")
def have_cretificate(self):
try:
# 查找父标签
parent_element = self.driver.find_element(By.CSS_SELECTOR,"div.course-left")
try:
# 在父标签下查找目标子标签
parent_element.find_element(By.CSS_SELECTOR, "div.certInfo")
# 如果找到子标签,返回True
return True
except NoSuchElementException:
# 未找到子标签
return False
except NoSuchElementException:
# 未找到父标签
return False
def is_video_playing_normally(self, timeout=5):
try:
pause_icon = WebDriverWait(self.driver, timeout).until(
EC.visibility_of_element_located(
(By.CSS_SELECTOR, "xg-play .xgplayer-icon .xgplayer-icon-pause") # 暂停图标
)
)
return pause_icon.is_displayed()
except TimeoutException:
# 超时说明任一条件未满足(加载中/未播放/控件未出现)
return False
def get_progress(self):
"""获取课程进度(已完成节数,总节数)"""
try:
finished_text = self.driver.find_element(By.CSS_SELECTOR, "span.section-finish").text
finished_num = int("".join(filter(str.isdigit, finished_text)))
except:
finished_num = 0
total_text = self.driver.find_element(By.CSS_SELECTOR, "span.section-total").text
total_num = int("".join(filter(str.isdigit, total_text)))
return finished_num, total_num
# ========== 核心功能 ==========
def login(self):
self.driver.get("https://sntelelearning.b.sanjieke.cn/login/sign_in")
username_input = self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input#rc_select_0"))
)
username_input.send_keys(self.username)
password_input = self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input.input-text[type='password']"))
)
password_input.send_keys(self.password)
login_btn = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.confirm-btn"))
)
login_btn.click()
print(" 登录成功")
def get_all_course_links(self, course_type):
"""翻页获取所有课程链接"""
card_module = self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.card-item-container"))
)
time.sleep(5)
# 找到课程卡片里的所有入口 <a class="card-item">
course_links = card_module.find_elements(By.CSS_SELECTOR, "a.card-item")
if course_links:
# 0为通用 1为专业
course_links[int(course_type) - 1].click()
print("点击第一个课程入口,进入学习页面。")
time.sleep(5)
# 获取所有 a 标签
all_links = [] # 存储所有 a 标签
while True:
self.wait.until(EC.presence_of_all_elements_located((By.TAG_NAME, "a")))
a_tags = self.driver.find_elements(By.TAG_NAME, "a")
for a in a_tags:
href = a.get_attribute("href")
text = a.text.strip()
if href and "/course/" in href:
all_links.append((text, href))
# 翻页
try:
next_btn = self.driver.find_element(By.CLASS_NAME, "ant-pagination-next")
if next_btn.get_attribute("aria-disabled") == "true":
break
else:
next_btn.click()
time.sleep(5)
except:
break
return all_links
def study_chapter(self):
"""学习单个章节/小节"""
try:
menu_container = self.driver.find_element(By.CSS_SELECTOR, "div.menu-container")
try:
chapters = menu_container.find_elements(By.CSS_SELECTOR, "div.chapter-container.chapter-item")
except:
chapters=[]
try:
sections = menu_container.find_elements(By.CSS_SELECTOR, "div.chapter-container.chapter-section-item")
except:
sections=[]
# 判断是否是大章节(有子小节)
if len(sections) > 0 :
for sid, section in enumerate(sections, start=1):
menu_container = self.driver.find_element(By.CSS_SELECTOR, "div.menu-container")
sections = menu_container.find_elements(By.CSS_SELECTOR,"div.chapter-container.chapter-section-item")
title = sections[sid - 1].find_element(By.CSS_SELECTOR, ".node-name-con").text.strip()
print(f" 进入大章节: {title}")
section_list = sections[sid - 1].find_elements(By.CSS_SELECTOR, ".section-container .node-item")
time.sleep(1)
self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", sections[sid - 1])
# 判断是否已点击
if "chapter-active" not in sections[sid - 1].get_attribute("class"):
sections[sid - 1].click()
# 找到子小节
for sec_idx, section_sec in enumerate(section_list, start=1):
menu_container = self.driver.find_element(By.CSS_SELECTOR, "div.menu-container")
sections = menu_container.find_elements(By.CSS_SELECTOR,"div.chapter-container.chapter-section-item")
sec_list = sections[sid - 1].find_elements(By.CSS_SELECTOR, ".section-container .node-item")
status_list = sections[sid - 1].find_elements(By.CSS_SELECTOR, ".section-container .status-con")
sec_title = sec_list[sec_idx - 1].find_element(By.CSS_SELECTOR, ".node-name-con").text.strip()
if "section-finish" in status_list[sec_idx - 1].get_attribute("class"):
status = "已完成 "
print(sec_title, status)
else:
status = "未完成 "
print(sec_title, status, "现在即将学习......")
self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", sec_list[sec_idx - 1])
time.sleep(2)
WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".section-container .node-item"))
)
sec_list[sec_idx - 1].click()
self.play_video(sec_title, 2, sec_idx, sid)
if len(chapters) > 0:
for idx, chapter in enumerate(chapters, 1):
menu_container = self.driver.find_element(By.CSS_SELECTOR, "div.menu-container")
chapters = menu_container.find_elements(By.CSS_SELECTOR, "div.chapter-container.chapter-item")
title = chapters[idx - 1].find_element(By.CSS_SELECTOR, ".node-name-con").text.strip()
# 判断是否完成
if "chapter-finish" in chapters[idx - 1].get_attribute("class"):
status = "已完成 "
print(title, status)
else:
status = "未完成 "
print(title, status, "现在即将学习......")
self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", chapters[idx - 1])
time.sleep(2)
WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".chapter-container .node-item"))
)
chapters[idx - 1].click()
self.play_video(title, 1, idx, 0)
except Exception as e:
print(f"⚠️ 小节异常,跳过: {e}")
def play_video(self, title, ctype, index, sid):
"""通用视频播放逻辑"""
try:
# 点击播放按钮
try:
# 定位视频容器,确保其在可视区域内
try:
video_container = WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.video-player-container")) # 视频容器选择器
)
# 滚动到视频容器可见(scrollIntoView() 会自动将元素滚动到可视区域)
self.driver.execute_script(
"arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});",
video_container
)
time.sleep(0.5) # 短暂等待滚动完成
except:
pass
play_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "xg-start .xgplayer-icon-play"))
)
play_btn.click()
time.sleep(1)
self.set_playback_rate_to_2x()
except:
play_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "xg-play .xgplayer-icon-play"))
)
play_btn.click()
time.sleep(1)
self.set_playback_rate_to_2x()
print(f" {title} 播放中...")
if not self.is_video_playing_normally():
return
# 模拟学习
# 增加最大播放时长限制(避免无限循环,可根据视频实际时长调整)
max_play_time = 1800 # 1小时超时
start_time = time.time()
while True:
# 检查是否超时
if time.time() - start_time > max_play_time:
print(f" {title} 播放超时(超过{max_play_time}秒)")
break
time.sleep(random.uniform(2, 3))
# 检测是否出现评价弹窗
self.handle_popup()
# 检测是否出现中断学习弹窗
try:
self.handle_leave_page_tip()
except:
pass
# 如果有AI选择
self.click_ai_option()
# 判断是什么类型的课程 section or chapter
menu_container = self.driver.find_element(By.CSS_SELECTOR, "div.menu-container")
if ctype == 1:
chapters = menu_container.find_elements(By.CSS_SELECTOR, "div.chapter-container")
if "chapter-finish" in chapters[index - 1].get_attribute("class"):
print(f" {title} 已完成")
break
else:
sections = menu_container.find_elements(By.CSS_SELECTOR, "div.chapter-container.chapter-section-item")
section_list = sections[sid - 1].find_elements(By.CSS_SELECTOR, ".section-container .node-item")
# 检查是否完成
if len(section_list[index - 1].find_elements(By.CSS_SELECTOR, "div.status-con.section-finish")) == 1:
print(f" {title} 已完成")
break
except Exception as e:
print(f" {title} 播放失败: {e}")
def study_course(self, href):
"""学习单个课程"""
self.driver.execute_script("window.open(arguments[0]);", href)
time.sleep(5)
# 切换到新标签页操作
self.driver.switch_to.window(self.driver.window_handles[-1])
try:
if self.have_cretificate():
print("该课程已完成!")
else:
self.driver.switch_to.window(self.driver.window_handles[-1])
h1_text = self.driver.find_element(By.TAG_NAME, "h1").text
study_btn = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "a.course-study-button"))
)
study_btn.click()
print(f" 开始学习课程: {h1_text}")
time.sleep(5)
finished_num, total_num = self.get_progress()
if finished_num >= total_num:
print(f" 课程 {h1_text} 已完成")
else:
self.study_chapter()
except Exception as e:
print(f" 课程异常,跳过: {href}, 错误: {e}")
finally:
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
if __name__ == "__main__":
bot = AutoCourseBot(USERNAME, PASSWORD)
bot.login()
links = bot.get_all_course_links(course_type)
for t, href in links:
bot.study_course(href)
解析
该脚本为三节课自动学习课程(刷时长)脚本,主要作用包括:
使用 Selenium + Chrome 自动登录学习平台(
sntelelearning.b.sanjieke.cn
),进入课程列表页,按配置选择课程类型(通用/专业),
逐个打开课程页,自动进入学习界面,
自动播放视频、倍速设置(2x)、周期性处理弹窗(中断提示/评价弹窗/AI题),
监控章节或小节的完成状态,循环播放直至"已完成",
支持翻页收集全部课程链接并逐一学习,直到完成或发生异常。
配置来源:
config.ini
(读取login.username/password
与course_type.type
)
主要方法
类:AutoCourseBot
封装全部自动学习流程的核心类。
__init__(self, username, password)
初始化 Chrome WebDriver(静音模式
--mute-audio
),最大化窗口、创建
WebDriverWait
,保存登录凭据。
登录与课程入口
login(self)
打开登录页,输入账号密码,点击登录按钮。成功后进入平台首页。get_all_course_links(self, course_type)
在课程卡片入口中点击对应分类(0:通用/1:专业,代码里用int(course_type)-1
),
然后在课程列表页遍历所有分页抓取包含/course/
的课程链接(标题, href)
并返回。
学习流程控制
study_course(self, href)
打开单个课程的新标签页 → 若已有证书直接跳过 → 否则点击"开始学习",
读取完成进度,如未完成则进入study_chapter()
执行章节学习。
结束后关闭标签并回到列表页。study_chapter(self)
定位左侧目录树,分别处理两种结构:大章节(有小节):逐个展开章节并遍历其中的小节;未完成的小节则点击进入并调用
play_video()
。普通章节:直接点击未完成的章节并调用
play_video()
。
过程中根据元素 class(如section-finish
/chapter-finish
)判断完成状态。play_video(self, title, ctype, index, sid)
通用的视频播放逻辑:评价弹窗(
handle_popup
),"学习状态中断"弹窗(
handle_leave_page_tip
),AI题随机选择(
click_ai_option
);每轮判断章节/小节是否已出现"完成"标记,完成则退出;
设有最长播放时长上限(默认 1800s)以免死循环。
滚动至视频区域,点击播放(尝试
xg-start
与xg-play
两种播放按钮);设为 2 倍速(
set_playback_rate_to_2x
);循环"学习中":定时(2~3s)检查并处理
页面元素与状态工具
handle_popup(self)
处理"评价/得分"等浮层:等待关闭按钮可点 → 点击 → 等待弹窗消失。handle_leave_page_tip(self, wait_time=5)
处理"学习中断提醒"的弹窗:点击"继续学习",并补一次播放按钮。click_ai_option(self)
如页面出现"AI题"(div.quiz-main
),随机点击一个选项(规避系统检测的拟人化操作)。set_playback_rate_to_2x(self, wait_time=5)
打开播放速度选择器并选择cname="2"
的 2x 项。have_cretificate(self)
在课程左侧信息区 (div.course-left
) 内查找div.certInfo
,判断课程是否已有证书(已完成)。is_video_playing_normally(self, timeout=5)
通过可见的暂停图标(播放中通常显示"暂停"按钮)判断视频是否处于播放状态。get_progress(self)
从页面读取已完成节数与总节数(解析span.section-finish
、span.section-total
文本中的数字)。
运行主流程
if __name__ == "__main__":
bot = AutoCourseBot(USERNAME, PASSWORD)
bot.login()
links = bot.get_all_course_links(course_type)
for t, href in links:
bot.study_course(href)
登录 → 2) 获取全部课程链接 → 3) 逐门学习,自动播放并处理弹窗/倍速,直至章节完成。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论