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=2019052.部署教程
3.代码如下
# -*- coding:utf-8 -*-import timeimport randomfrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.common.exceptions import NoSuchElementExceptionfrom selenium.common.exceptions import TimeoutExceptionfrom selenium.webdriver.chrome.options import Optionsimport 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 = usernameself.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:# passdef 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:passdef 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:passdef 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")# 如果找到子标签,返回Truereturn Trueexcept NoSuchElementException:# 未找到子标签return Falseexcept NoSuchElementException:# 未找到父标签return Falsedef 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 Falsedef get_progress(self):"""获取课程进度(已完成节数,总节数)"""try:finished_text = self.driver.find_element(By.CSS_SELECTOR, "span.section-finish").textfinished_num = int("".join(filter(str.isdigit, finished_text)))except:finished_num = 0total_text = self.driver.find_element(By.CSS_SELECTOR, "span.section-total").texttotal_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":breakelse:next_btn.click()time.sleep(5)except:breakreturn all_linksdef 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:passplay_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}秒)")breaktime.sleep(random.uniform(2, 3))# 检测是否出现评价弹窗self.handle_popup()# 检测是否出现中断学习弹窗try:self.handle_leave_page_tip()except:pass# 如果有AI选择self.click_ai_option()# 判断是什么类型的课程 section or chaptermenu_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} 已完成")breakelse: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} 已完成")breakexcept 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").textstudy_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) 逐门学习,自动播放并处理弹窗/倍速,直至章节完成。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论