1. 项目概述与核心价值最近在技术社区和论坛里经常看到有朋友在讨论如何自动化处理一些重复性的网页操作尤其是在线学习平台上的课程学习任务。这类需求背后其实是一个典型的“机器人流程自动化”RPA场景。今天我就以一个具体的实战项目为例聊聊如何用 Python 和 Selenium 这套经典组合快速构建一个能够自动完成网课学习的脚本。这个项目不是为了鼓励“偷懒”而是为了探讨在合法合规的前提下如何利用技术解放生产力将我们从枯燥、重复的点击和等待中解脱出来去处理更有价值的事情。比如你可以用这个时间去深入学习课程的核心内容或者完成更具创造性的作业。这个脚本的核心目标非常明确模拟一个真实用户在“安全微伴”这类在线学习平台上自动完成视频观看、章节切换、答题等学习流程。整个过程力求稳定、高效并且能够处理一些常见的反自动化检测机制。对于初学者而言这是一个绝佳的练手项目涵盖了 Web 自动化测试的多个核心知识点对于有经验的开发者也能从中获得一些关于反检测策略和代码健壮性设计的启发。接下来我将从设计思路到代码实现再到避坑指南为你完整拆解这个项目。2. 环境准备与工具选型解析2.1 为什么选择 Python Selenium在开始动手之前我们先聊聊技术选型。市面上能实现网页自动化的工具不少比如 PuppeteerNode.js、Playwright多语言支持等。我最终选择 Python Selenium主要基于以下几点考量首先生态与社区支持。Python 在自动化脚本和数据处理领域的生态极其丰富Selenium 作为老牌的 Web 自动化测试框架拥有庞大的用户群体和详尽的文档。这意味着你在开发过程中遇到的绝大多数问题都能在 Stack Overflow、GitHub 或中文技术博客上找到解决方案。对于快速原型开发和解决具体问题来说这是巨大的优势。其次学习曲线与灵活性。Python 语法简洁易于上手非常适合编写这种“胶水”脚本。Selenium 提供了近乎完整的浏览器操作 API从元素定位、表单填写到鼠标键盘模拟、弹窗处理一应俱全。它允许你以编程方式执行任何手动操作灵活性极高。虽然较新的 Playwright 在某些方面如自动等待、录制功能更优秀但 Selenium 的稳定性和普适性经过长期考验对于这个项目而言完全够用。最后可控性与调试便利性。Selenium 可以启动带有图形界面的真实浏览器如 Chrome、Firefox你可以直观地看到脚本每一步的执行效果这对于调试脚本、分析页面结构至关重要。相比之下纯后台无头Headless模式虽然更快但在开发初期可视化调试能节省大量时间。2.2 核心依赖安装与配置工欲善其事必先利其器。我们需要搭建一个可用的 Python 环境并安装必要的库。1. 安装 Python如果你的电脑上还没有 Python建议直接访问 Python 官网下载最新稳定版如 Python 3.10。安装时务必勾选“Add Python to PATH”选项这样可以在命令行中直接使用python和pip命令。安装完成后打开终端Windows 是 CMD 或 PowerShellMac/Linux 是 Terminal输入python --version来验证是否安装成功。2. 安装 Selenium 库Selenium 的 Python 客户端库可以通过 pip 一键安装。在终端中执行以下命令pip install selenium为了确保网络稳定你可以使用国内镜像源加速下载例如pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple3. 下载浏览器驱动WebDriver这是 Selenium 工作的关键。Selenium 需要通过一个名为 WebDriver 的组件来与具体的浏览器进行通信。你需要下载与你电脑上已安装的 Chrome 浏览器版本匹配的 ChromeDriver。查看 Chrome 版本打开 Chrome 浏览器在地址栏输入chrome://settings/help即可看到版本号。下载 ChromeDriver前往 ChromeDriver 的官方下载站点或国内镜像站。选择与你的 Chrome 主版本号一致的驱动进行下载。配置驱动路径下载后得到一个可执行文件如chromedriver.exe或chromedriver。你有两种方式让 Selenium 找到它方法一推荐便于管理将驱动文件放在一个固定的目录如C:\WebDriver\或~/bin/并将该目录添加到系统的环境变量PATH中。方法二简单直接在代码中指定驱动的绝对路径。我们后续的代码示例将采用这种方式因为它最明确不易出错。注意浏览器会频繁自动更新而 WebDriver 必须与浏览器版本匹配。如果某天脚本突然无法启动浏览器首先应该检查两者版本是否一致并及时更新驱动。3. 脚本核心设计与思路拆解在开始写代码之前我们需要对目标网站以“安全微伴”为例的操作流程进行“人肉”分析并将其转化为机器可执行的逻辑。这是整个项目中最重要的一步直接决定了脚本的稳定性和效率。3.1 用户操作流程模拟一个典型的手动刷课流程是怎样的我们拆解一下登录打开登录页面输入用户名和密码点击登录按钮。进入课程列表登录成功后通常会自动跳转或需要点击导航菜单进入“我的课程”或“学习中心”页面。选择课程在课程列表中找到需要学习的课程并点击进入课程详情页。遍历章节课程详情页会列出所有章节Chapter或模块Module。我们需要逐个点击进入。处理章节内任务进入一个章节后里面可能包含多种任务类型视频Video需要等待视频播放完毕或者模拟点击“播放”按钮并监测播放进度。文档Document可能需要模拟滚动页面或者等待一个固定的时间表示“已阅读”。测验Quiz需要自动答题。这可能是最复杂的部分涉及到题目识别、答案选择或输入。标记完成与跳转当前章节内所有任务完成后通常有一个“下一节”或“标记完成”按钮点击后进入下一个章节重复步骤5。循环与结束重复步骤4-6直到所有章节完成。我们的脚本就是要将上述7个步骤用代码精确地模拟出来。3.2 核心难点与应对策略在自动化过程中我们会遇到几个主要的挑战元素定位如何让代码准确地找到页面上的登录框、播放按钮、下一节按钮这是 Selenium 的基础也是成功的关键。我们将主要使用find_element方法配合多种定位策略如 ID、Class Name、XPath、CSS Selector。其中XPath 功能最强大但也要慎用因为页面结构一变就可能失效。等待机制网页加载需要时间元素不会立即出现。如果代码在元素出现前就去点击它就会抛出NoSuchElementException导致脚本崩溃。Selenium 提供了三种等待方式强制等待time.sleep简单粗暴但效率低下不推荐作为主要等待方式。隐式等待implicitly_wait设置一个全局的等待时间在查找元素时如果未立即找到会轮询等待直至超时。但它对元素的“可点击”、“可见”等状态无效。显式等待WebDriverWait这是最佳实践。它可以针对某个特定元素等待其满足某个条件如可见、可点击、存在后再进行后续操作灵活且高效。反自动化检测越来越多的网站会检测 Selenium 的自动化特征例如window.navigator.webdriver属性。如果被检测到可能会被阻止操作或要求验证。我们需要通过一些选项来“隐藏”这些特征。状态判断如何知道一个视频播放完了是等待固定时长还是去监测视频播放器的进度条如何判断一个测验是否已作答这需要仔细分析页面 DOM 结构寻找状态标识如完成的图标、变灰的按钮、进度文本等。4. 代码实现与核心环节解析下面我将分模块展示核心代码并解释每一部分的设计意图和关键点。请注意以下代码是一个高度概括的示例框架实际应用中需要根据目标网站的具体 HTML 结构进行调整。4.1 基础驱动配置与反检测策略首先我们初始化浏览器驱动并添加一些反检测和优化配置。from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By import time def create_driver(): chrome_options Options() # 反检测关键配置移除“自动化控制”特征 chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 可选添加其他常用选项 # chrome_options.add_argument(--headless) # 无头模式不显示浏览器界面 chrome_options.add_argument(--disable-blink-featuresAutomationControlled) chrome_options.add_argument(--start-maximized) # 启动时最大化窗口 chrome_options.add_argument(--disable-infobars) # 禁用“Chrome正受到自动测试软件的控制”提示 # 指定你的 chromedriver 路径 driver_path rC:\WebDriver\chromedriver.exe # Windows 示例 # driver_path /usr/local/bin/chromedriver # Mac/Linux 示例 service Service(executable_pathdriver_path) driver webdriver.Chrome(serviceservice, optionschrome_options) # 执行CDP命令覆盖 navigator.webdriver 属性 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); }) return driver代码解析excludeSwitches和useAutomationExtension是移除浏览器顶部黄条警告和禁用自动化扩展的基本操作。--disable-blink-featuresAutomationControlled是较新的 Chrome 版本中进一步隐藏自动化特征的方法。execute_cdp_cmd是核心中的核心。它通过 Chrome DevTools Protocol 在页面加载前注入一段 JavaScript将navigator.webdriver属性重写为undefined。这是目前绕过大多数基础检测最有效的手段之一。将驱动路径写死在代码里虽然不够优雅但对于个人使用的脚本来说最简单明了。你也可以通过环境变量来配置。4.2 登录模块实现登录是第一步也是最容易因页面变动而出错的一步。我们需要稳健的定位和等待策略。def login(driver, username, password): login_url https://你的学习平台登录页地址 driver.get(login_url) # 使用显式等待确保页面核心元素加载完成 wait WebDriverWait(driver, 10) # 最长等待10秒 try: # 定位用户名和密码输入框。这里需要根据实际页面HTML调整定位器。 # 假设通过ID定位这是最稳定的方式。 username_input wait.until(EC.presence_of_element_located((By.ID, username))) password_input driver.find_element(By.ID, password) submit_button driver.find_element(By.ID, submitBtn) # 清空输入框并输入凭据 username_input.clear() username_input.send_keys(username) time.sleep(0.5) # 短暂间隔模拟真人输入 password_input.clear() password_input.send_keys(password) time.sleep(0.5) # 点击登录 submit_button.click() # 等待登录成功后的页面跳转例如等待某个登录后才会出现的元素 wait.until(EC.presence_of_element_located((By.CLASS_NAME, user-avatar))) print(登录成功) return True except Exception as e: print(f登录过程出现错误{e}) # 这里可以添加截图功能方便调试 driver.save_screenshot(login_error.png) return False实操心得定位器优先级IDNameCSS SelectorXPath。ID 通常是唯一且最稳定的。如果元素没有 ID可以尝试用浏览器的开发者工具F12查看其 CSS 类或属性使用 CSS Selector。XPath 虽然强大但一旦页面结构微调比如多了一个div路径就可能失效。异常处理一定要用try...except包裹可能失败的操作并给出明确的错误提示和截图。这能让你在脚本无声无息失败时快速定位问题所在。等待策略WebDriverWait配合EC.presence_of_element_located是等待元素出现的标准做法。对于按钮有时需要等待其可点击EC.element_to_be_clickable。4.3 课程与章节遍历逻辑登录后我们需要找到目标课程并进入其学习界面。def enter_course(driver, course_name_keyword): 进入指定课程的学习页面 :param course_name_keyword: 课程名称中包含的关键字 # 假设登录后有一个“我的课程”链接 my_course_link WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, 我的课程)) ) my_course_link.click() # 等待课程列表加载 time.sleep(2) # 对于列表页有时简单的静态等待更可靠可根据实际情况调整 # 查找包含关键字的课程卡片或链接 # 这里使用XPath的contains函数进行模糊匹配 course_elements driver.find_elements(By.XPATH, f//*[contains(text(), {course_name_keyword})]) if not course_elements: print(f未找到包含‘{course_name_keyword}’的课程。) return False # 点击第一个匹配的课程 course_elements[0].click() print(f已进入课程{course_name_keyword}) # 等待课程详情页加载假设有一个章节列表的容器 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, chapter-list)) ) return True def study_all_chapters(driver): 遍历并学习所有章节 while True: # 1. 处理当前章节的所有任务视频、文档、测验 if not process_current_chapter(driver): print(处理当前章节时遇到问题退出。) break # 2. 尝试寻找并点击“下一章”或“下一节”按钮 try: # 按钮的文本可能多种多样“下一节”、“继续”、“Next Chapter” next_buttons driver.find_elements(By.XPATH, //button[contains(text(), 下一)]) if not next_buttons: next_buttons driver.find_elements(By.XPATH, //a[contains(text(), 下一)]) if next_buttons and next_buttons[0].is_enabled(): print(进入下一章节...) next_buttons[0].click() # 等待新章节内容加载 time.sleep(3) else: print(未找到可用的‘下一章节’按钮可能已学习完毕。) break except Exception as e: print(f切换章节时出错{e}) driver.save_screenshot(chapter_switch_error.png) break print(所有章节学习完成)关键点解析模糊查找使用contains(text(), ‘关键字’)的 XPath 可以让我们不必输入完整的课程名提高容错率。循环控制while True循环是遍历章节的核心。退出循环的条件有两个一是处理章节时失败process_current_chapter返回False二是找不到或无法点击“下一章节”按钮。按钮状态检查在点击“下一章节”按钮前使用is_enabled()方法检查按钮是否可用未被禁用。有些平台会在当前章节未完成时禁用该按钮。4.4 核心任务处理函数这是脚本的“大脑”需要根据不同的任务类型调用不同的处理函数。def process_current_chapter(driver): 处理当前章节内的所有任务 print(开始处理当前章节...) # 首先需要获取当前章节的所有任务项列表 # 假设每个任务项都有一个共同的类名如 ‘task-item’ try: task_items WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CLASS_NAME, task-item)) ) print(f本章节共有 {len(task_items)} 个任务。) except: print(未找到任务列表可能页面结构不同。) # 尝试直接处理当前页面内容可能是单页面课程 return process_single_page_task(driver) for index, task_item in enumerate(task_items): # 可能需要滚动到该任务项确保其在视口中 driver.execute_script(arguments[0].scrollIntoView({block: center});, task_item) time.sleep(0.5) # 判断任务类型并处理 task_type get_task_type(task_item) print(f 处理任务 {index1}: 类型[{task_type}]) if task_type video: handle_video_task(driver, task_item) elif task_type document: handle_document_task(driver, task_item) elif task_type quiz: handle_quiz_task(driver, task_item) else: print(f 未知任务类型跳过。) continue # 每个任务处理后稍作停顿模拟真人操作间隔 time.sleep(1) return True def get_task_type(task_element): 根据任务元素的特征判断其类型 # 这里需要根据目标网站的实际HTML结构来编写判断逻辑 # 例如视频任务可能包含一个视频图标或‘video’类 if task_element.find_elements(By.CLASS_NAME, video-icon): return video elif task_element.find_elements(By.CLASS_NAME, pdf-icon): return document elif task_element.find_elements(By.CLASS_NAME, quiz-icon): return quiz # 也可以通过链接文本判断 link_text task_element.text if 视频 in link_text: return video elif 文档 in link_text or 阅读 in link_text: return document elif 测验 in link_text or 练习 in link_text: return quiz else: return unknown4.5 具体任务处理器实现我们以最复杂的视频和测验任务为例。视频任务处理目标是让视频“播放”足够的时间或者直到进度完成。def handle_video_task(driver, task_element): 处理视频任务 # 点击进入任务详情页或直接播放 play_link task_element.find_element(By.TAG_NAME, a) play_link.click() time.sleep(2) # 等待播放页面加载 # 切换到可能出现的视频iframe如果视频是嵌入的 iframes driver.find_elements(By.TAG_NAME, iframe) if iframes: driver.switch_to.frame(iframes[0]) print( 已切换到视频iframe。) try: # 寻找播放按钮并点击 play_button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CLASS_NAME, play-button)) # 根据实际类名调整 ) play_button.click() print( 视频开始播放。) except: print( 未找到播放按钮可能自动播放或页面结构不同。) # 核心如何判断视频播放完成 # 方法1固定等待最简单但不精确 # video_duration 600 # 假设视频10分钟 # print(f 等待视频播放完成预计{video_duration}秒...) # time.sleep(video_duration) # 方法2监测进度条更精确但实现复杂 # 这里提供一个简化思路定期检查进度文本或进度条元素 total_wait_time 600 # 最大等待时间 interval 30 # 每30秒检查一次 for i in range(total_wait_time // interval): try: # 假设页面有一个显示“已观看100%”的元素 progress_element driver.find_element(By.CLASS_NAME, progress-text) if 100% in progress_element.text or 已完成 in progress_element.text: print( 视频播放完成。) break except: pass # 如果没找到进度元素忽略继续等待 print(f 播放中... 已等待{(i1)*interval}秒) time.sleep(interval) # 切换回主文档如果之前切换了iframe if iframes: driver.switch_to.default_content() # 返回任务列表页 driver.back() time.sleep(2)测验任务处理自动答题是难点通常需要预设答案或简单逻辑。def handle_quiz_task(driver, task_element): 处理测验任务 - 这是一个简化示例实际非常复杂 quiz_link task_element.find_element(By.TAG_NAME, a) quiz_link.click() time.sleep(3) print( 开始处理测验...) # 假设是单选题我们总是选择第一个选项 try: # 查找所有单选按钮或选项 options driver.find_elements(By.CSS_SELECTOR, input[typeradio]) for opt in options: if opt.is_enabled() and not opt.is_selected(): # 点击选项前的label通常比点击input本身更稳定 option_id opt.get_attribute(id) if option_id: label driver.find_element(By.CSS_SELECTOR, flabel[for{option_id}]) label.click() else: opt.click() time.sleep(0.2) print( 已选择所有单选题答案。) except Exception as e: print(f 处理单选题时出错{e}) # 处理填空题如果有 - 填入固定文本 try: text_inputs driver.find_elements(By.CSS_SELECTOR, input[typetext], textarea) for t_input in text_inputs: t_input.send_keys(已学习) time.sleep(0.2) print( 已填写所有填空题。) except Exception as e: print(f 处理填空题时出错{e}) # 提交答卷 try: submit_btn driver.find_element(By.XPATH, //button[contains(text(), 提交)]) submit_btn.click() time.sleep(2) # 处理提交后的确认弹窗等 confirm_btns driver.find_elements(By.XPATH, //button[contains(text(), 确认) or contains(text(), 确定)]) for btn in confirm_btns: if btn.is_displayed(): btn.click() time.sleep(1) print( 测验已提交。) except Exception as e: print(f 提交测验时出错{e}) # 返回 driver.back() time.sleep(2)重要警告handle_quiz_task函数中的自动答题逻辑是高度简化且具有破坏性的。在实际学习场景中强烈不建议使用这种随机或固定答案的方式完成测验。这违背了学习的初衷也可能违反平台规则。此处代码仅用于演示自动化交互的技术可能性。一个更道德且有用的自动化方向是编写脚本帮你从课程材料中搜索答案或者将你的答题思路与脚本结合实现半自动辅助答题。5. 完整脚本组装与主流程将上述所有函数组装起来并添加一些日志和错误处理就构成了我们的主程序。def main(): # 你的账号信息建议从配置文件或环境变量读取不要硬编码在代码中 USERNAME your_username PASSWORD your_password COURSE_KEYWORD 安全生产 # 你要学习的课程名称关键字 driver None try: print( 安全微伴自动学习脚本启动 ) driver create_driver() # 1. 登录 if not login(driver, USERNAME, PASSWORD): print(登录失败程序退出。) return # 2. 进入课程 if not enter_course(driver, COURSE_KEYWORD): print(进入课程失败程序退出。) return # 3. 学习所有章节 study_all_chapters(driver) print( 脚本执行完毕 ) except KeyboardInterrupt: print(\n用户中断执行。) except Exception as e: print(f程序运行过程中发生未预期错误{e}) import traceback traceback.print_exc() # 打印详细的错误堆栈 finally: if driver: # 脚本结束后可以选择关闭浏览器 # driver.quit() # 或者暂时不关闭方便查看最终状态 input(按回车键退出并关闭浏览器...) driver.quit() if __name__ __main__: main()6. 常见问题排查与优化技巧实录即使代码写得再仔细在实际运行中也会遇到各种意想不到的问题。下面是我在多次实践中总结的一些典型问题及其解决方案。6.1 元素定位失败NoSuchElementException这是最常见的问题没有之一。可能原因1页面尚未加载完成。解决方案在查找元素前增加等待。务必使用显式等待WebDriverWait替代硬性的time.sleep。确保等待的条件是元素可见或可点击而不只是存在。示例优化# 不佳的做法 time.sleep(5) element driver.find_element(By.ID, “myButton”) # 推荐的做法 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By wait WebDriverWait(driver, 10) # 最多等10秒 element wait.until(EC.element_to_be_clickable((By.ID, “myButton”)))可能原因2元素在 iframe 或 shadow DOM 中。解决方案如果元素位于iframe内必须先使用driver.switch_to.frame(frame_reference)切换到该 iframe 中才能定位其中的元素。操作完成后使用driver.switch_to.default_content()切回主页面。Shadow DOM 的处理更复杂可能需要执行 JavaScript 来穿透。可能原因3元素是动态生成的其属性如ID每次都会变化。解决方案使用相对定位或更稳定的属性。优先使用class name,tag name, 或者通过其父元素和兄弟元素关系来构造 XPath 或 CSS Selector。避免使用包含随机数字的 ID。示例假设一个按钮的类名总是btn-primary但它的父级div的类名是稳定的action-bar。# 不稳定的定位 driver.find_element(By.ID, “submit-123456”) # 更稳定的定位 driver.find_element(By.XPATH, “//div[class‘action-bar’]//button[class‘btn-primary’]”)6.2 被网站检测到自动化操作症状登录失败、出现验证码、页面无响应或直接返回错误信息。解决方案应用全套反检测配置确保在创建驱动时使用了我们之前提到的所有chrome_options配置和 CDP 命令。模拟人类行为在关键操作如点击、输入之间加入随机延迟使用random.uniform(0.5, 2)代替固定的time.sleep(1)。鼠标移动也可以加入随机轨迹。使用更真实的 User-Agent有些网站会检测 UA。可以定期更换 UA 字符串。考虑使用 undetected-chromedriver这是一个第三方库专门用于优化 Selenium 以避免被检测。它可以自动下载匹配的驱动并应用更高级的隐藏技巧。安装pip install undetected-chromedriver。使用方式与 selenium 类似但初始化更简单。import undetected_chromedriver as uc driver uc.Chrome()6.3 脚本运行不稳定时好时坏可能原因网络波动、页面加载速度不均、偶发性元素定位失败。解决方案增加重试机制对于关键操作如点击“下一章”可以封装在一个重试函数中。def click_with_retry(driver, locator, max_attempts3): for attempt in range(max_attempts): try: element WebDriverWait(driver, 5).until(EC.element_to_be_clickable(locator)) element.click() return True except Exception as e: print(f”点击尝试 {attempt1} 失败: {e}”) time.sleep(2) return False定期保存状态与日志将当前学习到的章节索引、课程ID等信息记录到一个本地文件或数据库中。如果脚本中途崩溃重启后可以从断点处继续而不是从头开始。使用更健壮的查找方式find_elements方法在找不到元素时会返回空列表而不是抛出异常。这可以用来做安全检查。elements driver.find_elements(By.CLASS_NAME, “some-class”) if elements: # 安全地操作 elements[0] pass else: print(“未找到元素执行备用逻辑”)6.4 浏览器窗口意外关闭或失去响应解决方案将主逻辑放在try...except...finally块中确保无论发生什么错误最后都能执行driver.quit()来清理浏览器进程防止残留的 chromedriver 进程占用内存。7. 项目总结与扩展思考这个项目从零开始搭建了一个能够自动处理在线学习平台任务的脚本框架。我们不仅实现了基础的页面导航、元素交互和状态判断还深入探讨了反检测、异常处理和代码健壮性等进阶话题。通过这个实践你应该对 Selenium 的核心 API、Web 自动化的常见模式以及调试排错的方法有了更深刻的理解。我个人在实际操作中的体会是这类自动化脚本的成功三分靠代码七分靠调试和对目标网站的分析。开发者工具F12中的 Elements 和 Console 面板是你最好的朋友。多花时间研究页面的 HTML 结构、网络请求和 JavaScript 行为往往比盲目修改代码更有效。最后再分享一个小技巧在编写这类脚本时可以分阶段进行。先手动在浏览器里完整走一遍流程并用开发者工具记录下关键节点的元素选择器。然后先实现登录和页面跳转确保基础通路是通的。接着再逐个攻破视频、文档、测验等具体任务模块。每完成一个模块就单独测试最后再整合起来。这种“分而治之”的策略能极大降低开发复杂度。这个脚本的潜力远不止于“刷课”。它的核心是一套通用的 Web 自动化流程框架。你可以稍加改造用于自动填写表单、定时签到、监控网页信息变化、进行简单的数据抓取在遵守robots.txt和网站条款的前提下等众多场景。希望这个项目能成为你探索 RPA 世界的一块有用的敲门砖。记住技术是工具如何使用它取决于你的智慧和责任心。