Python+Selenium自动化刷课脚本开发实战:从环境搭建到反爬策略

Python+Selenium自动化刷课脚本开发实战:从环境搭建到反爬策略 简介本资源是一款专为电子科技大学党员发展对象与积极分子设计的在线培训课程自动化学习辅助工具面向时间紧张、需高效完成党课学习任务的在校学生与教职工解决视频反复观看、手动答题耗时费力的核心痛点。压缩包共12个文件8.59MB含2个核心Python脚本jjfz.py与fzdx.py实现Selenium驱动浏览器自动播放视频与智能答题6张操作界面截图用于功能验证与流程说明另有Readme.md、说明文件.txt、附赠资源.docx及chromedriver.exe等配套文件覆盖环境配置、使用指引与扩展支持。目前已有50人学习下载提供开箱即用的无人值守学习方案包含完整可运行代码、清晰执行逻辑注释、典型页面元素定位策略及适配UESTC培训平台的实测参数助力用户在合规前提下显著提升学习效率。1. 项目缘起与核心需求当“刷课”成为刚需作为一名在高校信息化领域摸爬滚打了多年的技术人我见过太多为了完成各类“在线学习任务”而绞尽脑汁的师生。尤其是像党员发展对象、积极分子培训这类课程内容固然重要但形式往往固定——几十上百个视频每个视频必须播放到指定时长中间可能还穿插着一些选择题。手动操作不仅耗时耗力还容易因为忘记点击“下一节”而卡住进度。这个需求催生了一个非常具体的场景如何让电脑自动、合规地完成视频观看和答题流程我最近就为电子科技大学UESTC的这类在线培训平台用Python和Selenium库写了一个自动化脚本。它的目标很明确模拟一个真实用户的操作自动登录、按顺序播放所有视频、在视频播放完毕后自动跳转、遇到随堂测验则自动选择答案并提交最终实现“无人值守”完成整个学习流程。这听起来有点像“外挂”但其本质是浏览器自动化将重复、机械的人工操作转化为程序指令解放人的时间去做更有价值的事情。这个脚本非常适合有一定Python基础且面临类似在线学习平台“刷课”需求的朋友参考。2. 技术选型为什么是Python Selenium面对浏览器自动化市面上有不少工具比如PuppeteerNode.js、Playwright多语言支持等。我最终选择Python Selenium的组合是基于以下几个核心考量2.1 Selenium的核心优势对动态网页的强兼容性这类党员培训平台或大多数在线教育系统前端页面大量使用了JavaScript进行动态渲染。视频播放器、课程列表加载、答题交互等都不是简单的静态HTML。Selenium WebDriver的核心工作原理是直接控制浏览器如Chrome、Edge让浏览器真实地执行JavaScript并渲染出完整的DOM树然后我们再通过代码去查找元素、模拟点击。这确保了脚本能像真人一样“看到”并操作页面上所有动态生成的内容兼容性最强。2.2 Python的生态与上手门槛Python语法简洁拥有极其丰富的第三方库生态。对于这个项目而言除了Selenium我们可能还需要time库进行等待random库模拟人类操作间隔避免被反爬机制识别为机器人logging库记录运行日志以便排查问题。Python的快速原型开发能力让我们可以集中精力在业务逻辑如何找到播放按钮、如何判断视频是否播完上而不是纠结于复杂的语法。2.3 对比其他方案浏览器插件/油猴脚本优点是轻量、直接。但对于需要模拟完整登录流程、跨页面连续操作、以及处理复杂交互如答题的场景浏览器的插件API能力有限调试也不如本地脚本方便。Playwright微软出品后起之秀API设计更现代自动等待机制更好。但对于这类特定、传统的Web应用Selenium的社区资源遇到问题时搜索到的解决方案更丰富且稳定性经过了长时间考验。对于新手从Selenium入门浏览器自动化的资料也更多。纯HTTP请求理论上如果能分析出所有API接口用requests库直接发包会更高效。但这类学习平台的前后端交互通常非常复杂涉及大量的状态维护、Token验证逆向工程成本极高且一旦平台更新接口脚本很容易失效。Selenium模拟真实浏览器行为规避了复杂的协议分析鲁棒性更强。注意使用自动化脚本完成学习任务务必首先确认平台的使用条款。本脚本仅用于技术学习与交流旨在展示浏览器自动化技术的应用请勿将其用于任何可能违反规定或损害他人利益的行为。自动化操作应遵循“不影响服务器正常负载、不恶意刷取数据”的原则。3. 环境搭建与核心依赖安装工欲善其事必先利其器。要让Selenium跑起来我们需要准备三样东西Python环境、Selenium库、以及对应的浏览器驱动。3.1 Python环境配置如果你还没有安装Python建议直接访问Python官网下载最新稳定版本如Python 3.9。安装时务必勾选“Add Python to PATH”这样才能在命令行中直接使用python和pip命令。安装完成后打开命令行CMD或PowerShell输入python --version和pip --version能正确显示版本号即说明安装成功。网络上常见的报错“python不是内部或外部命令”或“pip不是内部或外部命令”都是因为安装时未添加PATH或系统环境变量问题。3.2 安装Selenium库Selenium库的安装非常简单使用pip一行命令即可pip install selenium为了更好的依赖管理我强烈建议使用虚拟环境。你可以使用venv# 在项目目录下 python -m venv venv # 激活虚拟环境 (Windows) venv\Scripts\activate # 激活后再安装selenium pip install selenium3.3 下载浏览器驱动——最关键的一步Selenium需要通过一个“驱动”来与真实的浏览器对话。这里以最常用的Chrome浏览器为例。查看Chrome版本打开Chrome点击右上角三个点 - 帮助 - 关于Google Chrome记下版本号例如115.0.5790.102。下载对应驱动访问ChromeDriver官方下载站或国内镜像站。驱动的版本必须与你的Chrome浏览器主版本号完全一致例如Chrome是115.x就找115.x.x.x的ChromeDriver。这是最常见的坑点版本不匹配会导致脚本无法启动浏览器或运行时出错。放置驱动下载的chromedriver.exe是一个可执行文件。你有两个选择放入系统PATH将其放在Python的安装目录下与python.exe同级或者任何已添加到系统环境变量PATH的目录中。指定路径在代码中通过webdriver.Chrome(executable_path‘你的驱动路径’)来指定。我推荐第一种更干净。对于Edge浏览器步骤类似需要下载msedgedriverFirefox则需要geckodriver。4. 脚本核心逻辑与代码逐行解析下面我将结合代码片段拆解整个自动化脚本的核心模块。请注意由于不同培训平台页面结构千差万别这里的代码是通用逻辑和关键方法的示例你需要根据目标网站的实际HTML结构进行调整。4.1 初始化驱动与登录模块脚本的第一步是启动浏览器并登录系统。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import logging # 配置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def login(driver, username, password, login_url): 登录函数 :param driver: 浏览器驱动实例 :param username: 学号/账号 :param password: 密码 :param login_url: 登录页面地址 try: driver.get(login_url) logging.info(正在访问登录页面...) # 显式等待等待用户名输入框出现最多等10秒 username_input WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, username)) # 这里需要替换为实际元素的ID或选择器 ) password_input driver.find_element(By.ID, password) # 同上 submit_button driver.find_element(By.ID, submit) # 同上 # 模拟输入和点击 username_input.clear() username_input.send_keys(username) time.sleep(random.uniform(0.5, 1.5)) # 随机等待模拟人类输入间隔 password_input.clear() password_input.send_keys(password) time.sleep(random.uniform(0.5, 1.5)) submit_button.click() logging.info(登录信息已提交等待页面跳转...) # 等待登录后的某个标志性元素出现比如“我的课程”标题 WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, //h2[contains(text(), 我的课程)])) ) logging.info(登录成功) return True except Exception as e: logging.error(f登录过程出现异常: {e}) return False关键点解析显式等待WebDriverWait这是Selenium自动化中最重要、最易出错的概念之一。网络有延迟页面元素加载需要时间。WebDriverWait会周期性地检查条件如元素是否出现直到条件满足或超时。绝对不要使用固定的time.sleep(10)这会造成时间浪费或等待不足。这里等待登录后页面元素是判断登录是否成功的可靠方法。元素定位By.ID, By.XPATH你需要使用浏览器的开发者工具F12来查看目标输入框、按钮的HTML属性。优先使用ID因为它是唯一的。如果没有ID可以使用name,class_name, 或者更灵活的XPATH和CSS_SELECTOR。//h2[contains(text(), ‘我的课程’)]是一个XPATH示例意思是查找所有h2标签且其文本内容包含“我的课程”的。随机等待time.sleep(random.uniform(...)))在关键操作之间插入随机的、短时间的等待可以极大地降低被网站识别为机器人的风险。这是模拟人类操作不可或缺的一环。4.2 导航至课程列表并进入学习页面登录成功后需要找到具体的培训课程并进入。def enter_course(driver): 进入指定课程的学习页面 try: # 假设课程列表在一个class为‘course-list’的div里课程链接包含‘党员培训’字样 course_link WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //a[contains(href, course) and contains(text(), 党员培训)])) ) course_link.click() logging.info(已进入课程主页...) # 等待课程页面加载并点击“开始学习”或类似按钮 start_learning_btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //button[contains(text(), 开始学习) or contains(text(), 进入学习)])) ) start_learning_btn.click() logging.info(已进入学习界面准备开始自动化学习...) return True except Exception as e: logging.error(f进入课程失败: {e}) return False这个函数的核心是定位到正确的课程链接和学习入口按钮。element_to_be_clickable条件比presence_of_element_located更严格它要求元素不仅存在还要是可点击状态避免了元素被遮挡或禁用时误点击的问题。4.3 核心中的核心视频播放与完成检测这是脚本最核心、也最需要针对不同平台适配的部分。核心思路是获取所有章节/视频列表 - 循环遍历 - 播放当前视频 - 检测播放是否完成 - 完成则进入下一个。def auto_play_videos(driver): 自动播放所有视频 # 1. 获取所有视频章节的列表 # 假设每个章节是一个li元素里面包含一个视频链接 chapter_items WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, .chapter-list li a.video-link)) ) total_chapters len(chapter_items) logging.info(f共发现 {total_chapters} 个视频章节。) for index in range(total_chapters): # 每次循环重新获取列表因为页面状态可能改变 chapters driver.find_elements(By.CSS_SELECTOR, .chapter-list li a.video-link) if index len(chapters): break current_chapter chapters[index] chapter_title current_chapter.text logging.info(f正在处理第 {index1}/{total_chapters} 章: {chapter_title}) # 2. 点击进入当前章节 current_chapter.click() time.sleep(3) # 等待视频页面加载 # 3. 查找视频播放器并点击播放 try: # 方式一查找播放按钮并点击 play_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, .vjs-big-play-button)) ) # 很多网站使用video.js播放器这是其大播放按钮的类 play_button.click() logging.info(已点击播放按钮。) except: # 方式二有些页面视频自动播放或者通过操作video元素本身 # 可以通过执行JavaScript来播放 try: video_element driver.find_element(By.TAG_NAME, video) driver.execute_script(arguments[0].play();, video_element) logging.info(通过JS指令开始播放视频。) except Exception as e: logging.warning(f无法找到或操作播放控件: {e}) # 可能不是视频页面或者是文本章节直接跳过 continue # 4. 监测视频播放完成 # 这是难点不同平台策略不同。常见方法有 # a) 检测播放进度条循环检查video元素的currentTime和duration属性 # b) 检测页面上的“已完成”标志如出现“✓”图标或“已完成”文字 # c) 检测“下一节”按钮变为可点击状态 # d) 最保守的方法固定等待一个视频的最大时长例如假设每个视频不超过60分钟 # 这里演示方法a和d的结合 max_wait_time 60 * 60 # 假设单个视频最长1小时 start_time time.time() video_completed False while (time.time() - start_time) max_wait_time: time.sleep(30) # 每30秒检查一次 try: video_elem driver.find_element(By.TAG_NAME, video) # 通过JavaScript获取视频当前时间和总时长 current_time driver.execute_script(return arguments[0].currentTime;, video_elem) duration driver.execute_script(return arguments[0].duration;, video_elem) # 如果视频总时长有效且当前播放进度超过总时长的95%认为播放完成 if duration 0 and (current_time / duration) 0.95: logging.info(f视频播放进度 {current_time:.1f}/{duration:.1f} 即将完成。) video_completed True break else: logging.debug(f播放进度: {current_time:.1f}/{duration:.1f}) except: # 可能不是HTML5 video标签尝试检测完成标志方法b try: completed_mark driver.find_element(By.XPATH, //span[contains(text(), 已完成) or contains(class, completed)]) if completed_mark.is_displayed(): logging.info(检测到‘已完成’标记。) video_completed True break except: pass # 继续等待 if not video_completed: logging.warning(f第 {index1} 章可能在 {max_wait_time/60} 分钟内未播放完成强制进入下一章。) # 5. 播放完成或超时后寻找并点击“下一章”或“下一节”按钮 try: next_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //a[contains(text(), 下一) or contains(class, next)])) ) next_button.click() logging.info(已进入下一章节。) time.sleep(random.uniform(2, 4)) # 跳转后等待页面稳定 except Exception as e: logging.error(f未找到‘下一章’按钮尝试返回章节列表。错误: {e}) # 如果找不到下一章按钮可能已到最后一章或者需要返回课程目录 driver.back() # 浏览器后退 time.sleep(3)关键点与踩坑实录动态元素列表在for循环中直接使用最初获取的chapter_items列表是危险的。因为点击进入视频再返回后页面DOM可能刷新旧的元素引用会失效StaleElementReferenceException。所以我在循环内每次都重新查找元素driver.find_elements(...)这是一个非常重要的技巧。播放完成检测这是整个脚本的“灵魂”也是最需要定制化的部分。没有一种方法能通吃所有平台。你必须亲自打开目标网站用开发者工具分析视频播放器是什么是原生video标签还是第三方播放器如video.js, DPlayer播放完成后页面UI有什么变化是出现一个对勾图标还是“下一节”按钮从灰色变亮能否通过JavaScript直接获取播放器的状态在开发者工具的Console里尝试document.querySelector(‘video’).duration看看。我个人的经验是采用“组合策略”优先尝试通过JS获取视频进度方法a如果不行则轮询查找完成标志方法b同时设置一个绝对超时时间方法d作为保底防止脚本永远卡住。“下一节”按钮的定位同样需要观察页面。按钮的文本可能是“下一节”、“下一章”、“Next”类名可能包含next。使用contains函数的XPATH可以增加容错性。4.4 自动答题功能实现许多培训视频中间或结尾会插入随堂测验。自动化答题的逻辑是获取题目和选项 - 根据策略选择答案 - 提交。def handle_quiz(driver): 处理弹出的随堂测验 try: # 1. 检测是否有测验弹窗或页面出现 quiz_container WebDriverWait(driver, 5).until( # 短时间等待可能没有测验 EC.presence_of_element_located((By.CSS_SELECTOR, .quiz-container, .exam-panel, [class*quiz])) ) logging.info(检测到随堂测验开始处理...) # 2. 获取所有题目 questions quiz_container.find_elements(By.CSS_SELECTOR, .question-item) for q in questions: question_text q.find_element(By.CSS_SELECTOR, .question-stem).text logging.info(f题目: {question_text[:50]}...) # 打印前50字符 # 3. 答题策略这里需要你根据实际情况定制 # 策略A如果知道答案可以建立题库映射 # 策略B对于选择题默认选择第一个选项或随机选一个 # 策略C查找选项中有“正确”、“是”、“对”等关键词的 options q.find_elements(By.CSS_SELECTOR, .option-label) if options: # 示例选择第一个选项 options[0].click() time.sleep(0.5) logging.info( 已选择第一个选项。) # 更复杂的策略可以在这里实现比如用文本匹配 # 4. 提交答案 submit_btn quiz_container.find_element(By.XPATH, .//button[contains(text(), 提交)]) submit_btn.click() logging.info(测验答案已提交。) time.sleep(2) # 等待提交结果 # 5. 处理提交后的反馈如关闭弹窗 try: close_btn driver.find_element(By.CSS_SELECTOR, .modal-close, .confirm-btn) close_btn.click() except: pass # 可能没有关闭按钮 return True except Exception as e: # 如果在5秒内没找到测验元素说明当前页面没有测验 logging.debug(f未检测到随堂测验或处理时出错: {e}) return False答题策略的思考全自动答题并保证100%正确率是非常困难的这涉及到自然语言处理NLP。在实际项目中通常采用更务实的方案题库本地映射如果题目是固定的可以手动做一遍把题目和正确答案保存在本地字典或数据库里脚本运行时进行匹配。这是最准确但初期工作量大的方法。关键词匹配对于判断题或明显有倾向的选择题在选项文本中搜索“正确”、“错误”、“是”、“否”等关键词。默认/随机选择对于无关紧要的测验或者允许错误的场景可以选择第一个选项或随机选一个。务必谨慎评估平台规则有些平台多次答题错误可能会锁定账号或需要人工解锁。人工干预兜底在脚本中设置当检测到测验时暂停自动化发出提醒如播放提示音、日志高亮等待人工处理后再继续。这是最安全的方式。4.5 主函数与流程控制最后我们将所有模块串联起来并增加必要的异常处理和日志记录。def main(): # 配置浏览器选项可选 options webdriver.ChromeOptions() # 可选无头模式不显示浏览器界面适合在服务器运行 # options.add_argument(--headless) # 可选禁用GPU加速避免一些兼容性问题 options.add_argument(--disable-gpu) # 可选禁用浏览器弹窗如“保存密码”提示 prefs {profile.default_content_setting_values.notifications: 2} options.add_experimental_option(prefs, prefs) # 初始化驱动 driver webdriver.Chrome(optionsoptions) # 如果驱动不在PATH需指定executable_path driver.implicitly_wait(10) # 设置隐式等待为所有find_element操作设置默认等待时间 wait WebDriverWait(driver, 15) # 显式等待对象 try: # 你的账号密码可以从配置文件或安全输入读取 USERNAME your_student_id PASSWORD your_password LOGIN_URL https://xxx.uestc.edu.cn/login # 替换为实际登录地址 # 1. 登录 if not login(driver, USERNAME, PASSWORD, LOGIN_URL): logging.error(登录失败程序退出。) return # 2. 进入课程 if not enter_course(driver): logging.error(进入课程失败程序退出。) return # 3. 开始自动化学习循环 auto_play_videos(driver) logging.info(所有章节学习完成) except Exception as e: logging.error(f主流程运行出错: {e}) # 可以在这里截图保存错误时的页面状态便于调试 driver.save_screenshot(ferror_{int(time.time())}.png) finally: # 无论是否出错最后都关闭浏览器 time.sleep(5) driver.quit() logging.info(浏览器已关闭。) if __name__ __main__: main()5. 实战中的高级技巧与避坑指南写一个能跑的脚本只是第一步写一个稳定、健壮、不易被发现的脚本才是真正的挑战。下面分享一些我踩过坑后总结的经验。5.1 对抗反爬与模拟真人行为即使只是自动化学习一些平台也会有简单的反机器人检测。随机化操作间隔在所有time.sleep()和操作之间使用random.uniform(a, b)来产生随机等待时间模拟人类反应的不确定性。模拟鼠标移动Selenium的ActionChains可以模拟更真实的鼠标轨迹。from selenium.webdriver.common.action_chains import ActionChains element driver.find_element(By.ID, some_button) actions ActionChains(driver) actions.move_to_element(element).pause(random.uniform(0.2, 0.8)).click().perform()使用不同的User-Agent可以通过options.add_argument(‘user-agent你的UA字符串’)来更换。避免高频请求不要在循环中不停地进行find_element操作合理设置检查间隔如检测视频进度每30秒一次。5.2 异常处理与脚本健壮性网络会波动页面元素会变化脚本必须能处理异常并从错误中恢复。广泛的Try-Except像上面的代码一样在每一个可能失败的步骤查找元素、点击、获取属性外包上try-except并记录详细的日志。设置全局超时与重试机制对于关键操作如登录可以封装一个带重试的函数。def retry_find_element(driver, by, value, retries3, delay2): for i in range(retries): try: element driver.find_element(by, value) return element except: if i retries - 1: logging.warning(f第{i1}次查找元素失败{delay}秒后重试...) time.sleep(delay) else: raise页面状态检查点在关键流程节点如登录后、进入课程后通过判断特定元素是否存在来确认流程是否走到了正确的位置。5.3 元素定位策略的优先级与容错优先级IDNameCSS SelectorXPath。ID通常最快、最唯一。复杂的XPath虽然强大但性能稍差且容易因页面微小改动而失效。相对路径与属性组合尽量使用相对路径和属性组合避免使用绝对路径。//div[class‘container’]//button[text()‘提交’]比/html/body/div[3]/div[2]/button要稳定得多。使用find_elements进行存在性判断当你不能确定一个元素是否存在时使用find_elements返回列表而不是find_element找不到会抛异常。如果列表为空则说明元素不存在。elements driver.find_elements(By.CLASS_NAME, “popup-close”) if elements: elements[0].click() # 如果存在关闭弹窗按钮则点击5.4 调试与日志详细的日志是调试的救命稻草。使用Python的logging模块设置不同的级别INFO, DEBUG, WARNING, ERROR。在开发阶段可以将级别设为DEBUG查看所有细节运行时设为INFO只记录关键步骤。 在关键步骤失败时如登录失败使用driver.save_screenshot(‘error.png’)保存截图能直观地看到失败时浏览器渲染到了哪一步。6. 安全、合规与伦理边界最后我必须再次强调技术应用的边界。开发和使用这类脚本时请务必清醒地认识到以下几点目的正当性这个脚本的初衷是自动化处理形式化、耗时的流程将人的时间解放出来用于实质性的学习和思考。它不应被用于恶意刷分、攻击服务器或干扰平台正常运行。尊重平台规则仔细阅读学习平台的使用条款。有些平台明确禁止任何形式的自动化工具。使用脚本可能导致账号被警告、限制甚至封禁。你需要自行评估风险。技术学习价值抛开具体应用场景这个项目本身是一个绝佳的Selenium自动化学习案例。它涵盖了元素定位、等待机制、异常处理、模拟交互等核心知识点对于学习Web自动化测试或爬虫技术都有很大帮助。信息与账号安全脚本中会硬编码或读取你的账号密码。切勿将包含真实账号信息的脚本上传到GitHub等公开仓库。可以使用配置文件如config.ini来管理敏感信息并将配置文件加入.gitignore。这个项目从构思到实现再到不断调试优化让我对浏览器自动化有了更深刻的理解。最大的体会是自动化不是万能的它最适合规则明确、重复性高的流程。面对千变万化的真实网页最大的挑战往往不是代码本身而是如何让程序像人一样去“理解”和“适应”不完美的界面。每一次定位元素的失败每一次等待超时都是对代码健壮性的一次考验。如果你也打算尝试类似的项目我建议从最简单的“自动登录”开始逐步增加功能模块耐心调试你收获的将不仅仅是一个省时间的工具更是一套解决实际问题的工程化思维。本文还有配套的精品资源点击获取