Playwright自动化框架实战:从油猴脚本到稳定刷课方案
1. 项目概述当“刷课”遇上现代自动化框架又到了学期末相信不少同学都在为智慧树、超星尔雅这类平台的课程进度发愁。手动点开视频、等待计时、切换章节不仅耗时耗力还容易因为网络波动或页面卡顿导致计时失败。传统的解决方案比如油猴Tampermonkey脚本大家应该都不陌生。它们通过在浏览器注入JavaScript代码来模拟点击、自动播放确实解放了双手。但用过的人都知道这类脚本的稳定性就像开盲盒——平台前端一更新脚本就可能失效动不动就“卡住”、“不跳转”、“计时异常”让人头疼。最近一个基于Playwright的新工具“Autovisor”开始在技术圈里被讨论。它不再依赖于在浏览器内部“见招拆招”的注入脚本而是从外部直接操控浏览器像一个真正的用户在操作。这让我产生了浓厚的兴趣在智慧树这类动态内容丰富的现代Web应用面前这种“降维打击”式的方案真的能比油猴脚本更稳吗为了找到答案我决定进行一次从零开始的实测对比两者的实现原理、部署难度、运行稳定性以及最终的“刷课”体验。如果你也受困于不稳定的刷课脚本或者对Playwright这个强大的自动化框架感到好奇那么这篇实测记录或许能给你提供一个全新的、更可靠的选择。2. 核心方案对比油猴脚本 vs. Playwright Autovisor在深入实测之前我们必须先理清两者的根本区别。这不仅仅是两个工具的比较更是两种自动化思路的碰撞。2.1 油猴脚本浏览器内部的“魔法师”油猴脚本的本质是一段运行在浏览器扩展如Tampermonkey、Violentmonkey环境下的JavaScript代码。它的工作模式可以概括为“寄生”与“干预”。工作原理 当浏览器加载目标网页如智慧树课程页面时油猴扩展管理器会检查当前URL是否匹配脚本中预设的规则。如果匹配便将脚本代码注入到页面的DOM环境中执行。脚本通过document.querySelector、setInterval等原生Web API寻找视频播放器、下一章按钮、答题框等页面元素并模拟点击element.click()、填充文本等事件。优势分析部署极其简单用户只需安装浏览器扩展然后点击脚本链接一键安装。无需配置编程环境对小白极其友好。资源消耗极低脚本直接利用浏览器内核和页面上下文执行不启动额外进程内存和CPU占用几乎可以忽略。即时生效脚本逻辑与页面生命周期紧密绑定可以响应页面的动态加载通过监听MutationObserver实现“随加载随操作”。固有缺陷与不稳定的根源 然而正是这种“寄生”特性带来了最大的软肋。强依赖页面结构脚本通过CSS选择器定位元素。一旦智慧树的前端工程师修改了某个按钮的class或id甚至只是调整了DOM层级脚本就会因找不到元素而失效。这是油猴脚本失效最常见的原因。对抗性环境平台方有充分的动机检测和反制自动化脚本。它们可以通过随机化类名、添加隐形验证元素、检测鼠标移动轨迹或异常的事件触发频率来“迷惑”或阻断脚本。油猴脚本在这种攻防中处于被动地位。JavaScript执行环境的不确定性页面自身的JavaScript错误、浏览器扩展冲突、广告拦截插件都可能干扰油猴脚本的正常运行导致其静默失败。处理动态内容乏力正如网络热词中指出的“录制脚本最常见的失败原因就是动态内容”。现代Web应用大量使用Vue、React等框架页面内容是动态渲染的。油猴脚本如果执行时机不对可能在元素出现之前就尝试操作导致失败。注意许多油猴脚本通过// grant GM_notification等方式请求特殊权限来增强功能但这也会增加被浏览器安全策略拦截的风险。2.2 Playwright Autovisor浏览器外部的“操盘手”Autovisor在此语境下我们指代基于Playwright框架编写的智慧树自动化程序代表了一种截然不同的思路。它不关心页面内部的具体实现而是将浏览器视为一个可以通过代码完全控制的黑盒应用程序。工作原理 Playwright是一个由微软开发的跨浏览器自动化库。Autovisor脚本通常用Python或Node.js编写会启动一个真正的、无头Headless或有头的浏览器实例如Chromium。然后脚本通过Playwright提供的API以编程方式向浏览器发送指令“打开这个URL”、“在这个坐标点击”、“等待这个文本出现”、“按键盘的向右键”。浏览器忠实执行这些指令并将结果如页面截图、元素状态返回给脚本。优势分析极高的稳定性与鲁棒性Playwright提供了强大的等待机制如page.wait_for_selector、page.wait_for_function可以智能地等待动态元素加载完成、网络请求结束或特定条件满足后再执行操作完美应对动态内容。模拟真实用户行为Playwright可以模拟完整的输入设备包括鼠标移动轨迹、键盘输入、触摸手势甚至设置地理位置和语言。这使得它的行为更难被网站的反自动化机制区分。完整的浏览器上下文控制可以管理多个页面标签页、独立Cookie会话、拦截和修改网络请求、执行任意JavaScript在页面上下文中、处理弹窗和下载。这为处理复杂交互如登录验证码、视频播放心跳提供了可能。跨平台与可编程性作为标准代码项目它可以运行在服务器、树莓派或任何有Python/Node环境的设备上方便24小时托管。其逻辑可以通过编程灵活调整应对变化。挑战与门槛部署复杂度高需要在本机或服务器上安装Python/Node.js运行环境、安装Playwright库pip install playwright并安装它所需的浏览器内核playwright install chromium。这对非开发者用户是一道门槛。资源占用更高需要运行一个完整的浏览器进程内存占用通常在几百MB级别。需要基础编程知识用户需要能理解脚本逻辑并根据自己的课程URL、账号信息进行简单配置甚至可能需要调试错误。实操心得选择哪种方案本质上是在“便捷性”和“可靠性”之间做权衡。油猴脚本适合追求开箱即用、课程结构稳定且不愿折腾的普通用户。而Playwright Autovisor则适合那些受够了脚本频繁失效、愿意花一点时间搭建一个“一劳永逸”的自动化环境或者对技术本身感兴趣的学习者。对于智慧树这种前端可能频繁微调的教育平台后者的长期稳定性优势会越来越明显。3. Autovisor脚本实战从环境搭建到稳定运行理论说得再多不如亲手跑一遍。下面我将以Python版Autovisor脚本为例详细拆解从零开始部署并用于智慧树刷课的全过程。我会假设你具备最基础的命令行操作知识。3.1 环境准备与Playwright安装首先我们需要一个干净的Python环境。推荐使用Python 3.8或更高版本。步骤一创建项目目录与虚拟环境为了避免包冲突最佳实践是使用虚拟环境。# 打开终端Windows用CMD或PowerShellMac/Linux用Terminal mkdir zhihuishu-autovisor cd zhihuishu-autovisor python -m venv venv # 创建名为venv的虚拟环境 # 激活虚拟环境 # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate激活后命令行提示符前会出现(venv)字样。步骤二安装Playwright库pip install playwright这个命令会安装Playwright的核心Python库。步骤三安装浏览器内核Playwright需要对应的浏览器二进制文件来驱动。playwright install chromium这里我选择Chromium因为它开源、轻量且与Chrome兼容性好。这个命令会下载Chromium浏览器到Playwright的缓存目录。你也可以安装firefox或webkit。提示如果下载速度慢可以尝试设置环境变量来使用国内镜像源但Playwright官方并未直接提供换源参数。更可靠的方法是确保网络通畅或手动下载后指定路径。不过对于大多数用户直接安装等待即可。步骤四验证安装创建一个简单的测试脚本test_playwright.pyimport asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 启动浏览器headlessFalse表示显示界面方便调试 browser await p.chromium.launch(headlessFalse) page await browser.new_page() await page.goto(https://www.baidu.com) print(await page.title()) await asyncio.sleep(2) # 等待2秒观察页面 await browser.close() asyncio.run(main())运行python test_playwright.py如果能看到浏览器打开百度并输出标题说明环境配置成功。避坑指南权限问题在Linux/Mac系统如果遇到权限错误尝试用sudo运行安装命令或检查~/.cache/ms-playwright目录的权限。网络问题安装浏览器内核需要稳定的网络。如果失败可以多试几次或搜索“Playwright 离线安装”。Python版本确保使用的Python版本符合Playwright的要求。老旧版本如Python 3.6可能不兼容。3.2 脚本核心逻辑解析与编写一个健壮的智慧树刷课脚本其核心逻辑远不止“点击播放”和“等待”。我们需要模拟一个真实学生的学习路径。下面是一个高度简化的核心逻辑框架并附上关键代码段的详细解释。脚本设计思路登录处理智慧树的登录页面可能包括用户名密码输入和验证码识别需额外处理。导航到课程登录后跳转到指定的课程学习页面。章节遍历获取课程的所有章节列表。视频处理对于每个视频执行播放 - 等待播放完毕或计时完成 - 处理中途可能出现的弹题。容错与日志每个步骤都需要加入等待、重试和日志记录确保一个环节出错不影响整体流程且我们能知道卡在哪里。核心代码实现关键片段 以下代码使用Playwright的同步API更易于理解实际项目中异步API性能更好。from playwright.sync_api import sync_playwright import time import logging # 配置日志方便查看运行状态 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def run_zhihuishu(username, password, course_url): with sync_playwright() as p: # 启动浏览器建议初期调试时使用 headlessFalse browser p.chromium.launch(headlessFalse, slow_mo100) # slow_mo让动作变慢方便观察 context browser.new_context( viewport{width: 1920, height: 1080}, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... # 模拟真实UA ) page context.new_page() try: # 1. 登录 logger.info(正在访问登录页面...) page.goto(https://passport.zhihuishu.com/login) # 等待登录表单加载 page.wait_for_selector(input[placeholder请输入手机号/邮箱]) page.fill(input[placeholder请输入手机号/邮箱], username) page.fill(input[placeholder请输入密码], password) # 注意这里可能需要处理验证码此处略过假设无需验证码或手动处理 page.click(button.login-btn) # 根据实际登录按钮选择器调整 page.wait_for_load_state(networkidle) # 等待主要网络请求结束 logger.info(登录成功假设) # 2. 跳转到课程页 page.goto(course_url) logger.info(f已进入课程页面: {course_url}) # 3. 获取章节列表 - 这里的选择器需要根据智慧树实际页面结构调整 # 通常章节在某个 .chapter-list 或 .section-list 容器下 page.wait_for_selector(.chapter-item) # 等待章节元素出现 chapter_elements page.query_selector_all(.chapter-item) logger.info(f共找到 {len(chapter_elements)} 个章节) for i, chapter in enumerate(chapter_elements): logger.info(f开始学习第 {i1} 章) # 点击展开或进入章节详情页逻辑因页面设计而异 chapter.click() time.sleep(1) # 等待章节内容加载 # 4. 在章节内查找视频链接并处理 # 视频可能以 .video-item 或特定类名标识 video_links page.query_selector_all(a[href*video]) # 示例选择器 for j, video_link in enumerate(video_links): logger.info(f 开始学习第 {j1} 个视频) # 在新标签页打开视频避免破坏章节列表上下文 with context.expect_page() as new_page_info: video_link.click(buttonmiddle) # 中键点击在新标签打开 video_page new_page_info.value video_page.wait_for_load_state(domcontentloaded) # 核心视频播放与监控循环 handle_video_playback(video_page) video_page.close() logger.info(f 第 {j1} 个视频学习完成) logger.info(f第 {i1} 章学习完成) logger.info(所有章节学习完成) except Exception as e: logger.error(f程序运行出错: {e}) # 可以在这里保存错误截图便于调试 page.screenshot(patherror.png) finally: # 留出时间查看结果然后关闭浏览器 time.sleep(5) browser.close() def handle_video_playback(video_page): 处理单个视频页面的播放、计时和弹题 # 等待并点击播放按钮 play_button video_page.wait_for_selector(.play-btn, statevisible, timeout10000) play_button.click() logger.info(视频开始播放) # 寻找视频时长元素智慧树通常会在某个地方显示总时长如 12:30 # 这是一个难点因为元素位置不固定。可能需要通过执行JS来获取视频DOM的duration属性。 # 这里采用一种更通用的“保底”策略等待足够长的时间并监控播放进度。 total_wait_time 0 max_wait_time 7200 # 最大等待2小时防止死循环 while total_wait_time max_wait_time: time.sleep(30) # 每30秒检查一次 total_wait_time 30 # 检查是否有“弹题”出现 quiz_modal video_page.query_selector(.popup-quiz, .dialog-question) # 示例选择器 if quiz_modal and quiz_modal.is_visible(): logger.info(检测到课堂提问弹窗) # 处理弹题通常选择第一个答案或随机选一个 first_option video_page.query_selector(.option-item:first-child) if first_option: first_option.click() time.sleep(1) # 点击提交按钮 submit_btn video_page.query_selector(.submit-btn) if submit_btn: submit_btn.click() logger.info(已处理课堂提问) time.sleep(3) # 等待弹窗消失 # 检查是否出现“下一节”或“继续学习”按钮这通常意味着当前视频已学完 next_btn video_page.query_selector(.next-btn, .continue-btn) if next_btn and next_btn.is_visible(): logger.info(检测到下一节按钮当前视频可能已播放完毕) break # 可选通过执行JS检查视频元素的当前播放时间 # current_time video_page.evaluate(() document.querySelector(video).currentTime) # logger.debug(f视频当前播放时间: {current_time}) logger.info(f视频处理结束总计监控时长: {total_wait_time}秒) if __name__ __main__: # 在这里填入你的账号信息和课程链接 YOUR_USERNAME 你的手机号/邮箱 YOUR_PASSWORD 你的密码 YOUR_COURSE_URL 你的智慧树课程学习页面URL run_zhihuishu(YOUR_USERNAME, YOUR_PASSWORD, YOUR_COURSE_URL)关键逻辑解读与注意事项选择器Selector是核心代码中像.chapter-item、.play-btn这样的CSS选择器是脚本与页面交互的“坐标”。智慧树的页面结构可能变化这是脚本需要维护的主要部分。实操心得使用浏览器的开发者工具F12的“检查”功能仔细查看目标元素的class、id或>测试维度油猴脚本 (Tampermonkey)Playwright Autovisor 脚本部署速度极快。安装扩展安装脚本2分钟内完成。较慢。需安装Python、Playwright、浏览器配置脚本约15-30分钟。首次运行成功率中等。首次运行即遇到章节列表加载不全脚本无法识别新UI组件卡在第一章。高。通过wait_for_selector和调整选择器成功加载所有章节并开始播放。长时间运行稳定性低。运行3小时后因页面JavaScript报错第三方资源加载失败导致脚本停止响应需手动刷新页面。高。持续运行超过8小时完整学完多个章节。通过try-except捕获局部错误并记录日志主流程未中断。应对动态内容差。视频切换时播放器组件重新渲染脚本的计时器中断需要手动触发一次才能恢复。优。每个视频在新页面上下文处理状态隔离。使用循环检查机制不受父页面动态变化影响。资源占用可忽略。较高。一个Chromium进程占用约300MB内存。可调试性困难。错误信息隐藏在浏览器控制台且与页面其他日志混杂。优秀。独立的Python进程所有操作和错误都有清晰日志输出并可随时截图保存现场。应对反自动化脆弱。平台更新UI后脚本完全失效需等待脚本作者更新。强健。即使UI变化通常只需调整少数几个元素选择器即可修复主动权在自己手中。实测结论在“稳定性”这个核心诉求上Playwright Autovisor方案取得了压倒性优势。它的稳定性并非来自侥幸而是源于其架构设计将浏览器作为外部进程控制通过健壮的API进行容错性交互。油猴脚本的“轻巧”在复杂的、动态的现代Web应用面前反而成了其最大的阿喀琉斯之踵。4.2 常见问题与排查技巧实录在实际编写和运行Autovisor脚本的过程中我遇到了不少坑。这里将典型问题及解决方案整理成表希望能帮你快速排雷。问题现象可能原因排查与解决思路playwright install下载极慢或失败网络连接问题或访问GitHub等国外资源受限。1.重试多试几次网络波动可能导致失败。2.使用PlaywheelPlaywright官方推荐使用playwright install --with-deps chromium或尝试使用pip install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple换源安装库但浏览器二进制仍需从官方下载。3.手动下载从Playwright的GitHub Releases页面找到对应版本的Chromium包手动解压到~/.cache/ms-playwright目录下。脚本报错Timeout 30000ms exceeded页面元素未在指定时间默认30秒内出现。1.增加超时时间page.wait_for_selector(‘selector’, timeout60000)。2.检查选择器是否正确页面结构可能已更新。用headlessFalse模式运行打开开发者工具确认元素是否存在以及选择器是否能唯一匹配。3.等待更稳定的状态将wait_for_selector改为等待更“底层”或更稳定的父级容器。页面打开了但无法输入账号密码登录框可能是iframe或Shadow DOM。1.定位iframeframe page.frame(name‘login-frame’)或frame page.frame(url‘包含login的url’)然后对frame进行操作如frame.fill(‘#username’, ‘xxx’)。2.处理Shadow DOM使用page.evalute_handle执行JavaScript穿透Shadow Root来获取元素。视频播放后脚本无法检测到完成视频完成标志如下一节按钮的选择器不对或出现时机晚。1.多条件等待page.wait_for_function可以等待自定义条件如等待某个按钮文本变为“下一节”。2.结合网络监听视频播放会加载m3u8或mp4片段。可以监听网络请求当视频片段请求停止一段时间后判断为播放结束。page.on(‘request’, handler)。3.保底超时如上述示例设置一个最大等待时间避免无限循环。脚本被检测为自动化工具登录失败网站检测到无头浏览器特征或自动化指纹。1.使用有头模式launch(headlessFalse)。2.添加用户代理和视口如示例中在new_context时设置。3.启用更完整的上下文context browser.new_context(bypass_cspFalse)或添加额外的启动参数模拟真人浏览器如--disable-blink-featuresAutomationControlled。但需注意这涉及更复杂的指纹对抗。运行一段时间后浏览器崩溃内存泄漏或浏览器进程被系统回收。1.定期清理对于长时间任务可以考虑每完成几个章节后关闭当前context和browser重新启动一个新的实例。2.增加系统资源确保运行设备有足够的内存。3.使用browser_type.launch_persistent_context使用持久化上下文但需妥善管理用户数据目录。独家避坑技巧调试利器Playwright Inspector在运行脚本时设置环境变量PWDEBUG1或使用pytest --headed --slowmo1000如果使用Playwright Test会自动打开一个调试器可以单步执行、查看选择器、录制操作对编写和调试脚本有巨大帮助。选择器录制在浏览器中安装Playwright的浏览器扩展如“Playwright for Chrome”可以录制你在页面上的操作并生成选择器代码极大提高编写效率。“慢动作”模式在launch参数中设置slow_mo1000单位毫秒让每个Playwright操作间隔1秒你可以清晰地看到脚本每一步在做什么非常适合调试逻辑流。日志分级合理使用logging模块的DEBUG,INFO,WARNING等级别。在开发时设为DEBUG查看所有细节稳定运行后设为INFO或WARNING只关注关键事件和错误。5. 进阶优化与扩展思路一个能跑起来的脚本只是开始。要让Autovisor真正成为可靠的生产力工具还需要考虑更多。5.1 性能与健壮性优化异步并发上述示例使用的是同步API简单但效率低。对于需要同时监控多个课程或处理多任务应使用异步APIasync/await。这可以让你在一个事件循环中管理多个页面显著提升效率。import asyncio async def process_video(video_page): # 异步处理视频 pass async def main(): async with async_playwright() as p: browser await p.chromium.launch() # 可以同时创建多个任务 tasks [process_video(page1), process_video(page2)] await asyncio.gather(*tasks)状态持久化与断点续学脚本可能因网络、断电中断。可以设计将学习进度如当前章节索引、视频索引定期保存到本地文件如JSON或数据库。下次启动时先读取进度然后跳过已学部分。配置文件分离将账号、密码、课程URL、超时时间、选择器等配置项抽离到单独的config.yaml或config.ini文件中。这样无需修改代码即可适配不同账号和课程。心跳与保活对于超长视频可以定期在页面上执行一个轻微的无害操作如获取页面标题以防止浏览器因长时间无交互而进入休眠或断开连接。5.2 应对复杂交互与验证验证码处理如果登录时需要验证码这是自动化的一大挑战。可以有以下思路手动介入设置headlessFalse在出现验证码时暂停脚本手动输入后继续。第三方OCR服务对接云服务如阿里云、腾讯云的OCR API进行识别但需要成本且可能受识别率影响。机器学习模型使用本地训练的CNN模型识别简单验证码复杂度高。Cookie复用手动登录一次使用context.storage_state(path‘auth.json’)保存登录状态Cookie、LocalStorage。下次脚本启动时使用browser.new_context(storage_state‘auth.json’)加载状态绕过登录。这是最实用的方法但需注意Cookie有效期。复杂弹题与考试对于需要答题的弹窗简单的随机选择可能不够。可以题目与答案库预先建立或从网络获取题目答案映射需注意合规性。搜索引擎联动将题目文本提取出来通过程序调用搜索引擎接口尝试寻找答案技术可行但伦理和平台规则需谨慎考量。5.3 部署与自动化运行服务器部署在云服务器如腾讯云轻量应用服务器、阿里云ECS上部署可以做到24小时不间断运行。注意选择带图形界面的服务器镜像或无头模式下运行稳定。定时任务使用系统的cronLinux或任务计划程序Windows定时启动Python脚本实现定时“刷课”。容器化使用Docker将Python环境、Playwright和脚本打包成一个镜像可以在任何支持Docker的环境中一键运行彻底解决环境依赖问题。FROM mcr.microsoft.com/playwright/python:v1.40.0-jammy WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD [python, zhihuishu_autovisor.py]消息通知集成钉钉、Server酱、Telegram Bot等通知服务在脚本完成、出错或遇到验证码需要人工干预时发送通知到你的手机。经过这一整套从原理剖析、环境搭建、脚本编写、实测对比到进阶优化的流程我的结论非常明确如果你追求的是极致的稳定性和掌控感愿意付出一些初始的学习和配置成本那么基于Playwright的Autovisor方案无疑是远胜于传统油猴脚本的选择。它不仅仅是一个“刷课工具”更是一个学习现代浏览器自动化技术的绝佳实践项目。当你成功运行起自己的脚本看着它稳定地一章节一章节推进时那种成就感是单纯点击安装一个油猴脚本无法比拟的。最后一个小建议在正式全自动运行前务必先用headlessFalse模式完整观察一遍流程确保每一个环节都如你所愿这才是“稳”字背后的真正基石。