news 2026/10/4 2:46:02

我给学习通做了个「自动学习」系统:云端定时刷 + 本地 exe,全程真实播放、证据可审计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我给学习通做了个「自动学习」系统:云端定时刷 + 本地 exe,全程真实播放、证据可审计

项目地址:https://github.com/Thy985/xuexitong(纯开源,Python + Playwright + GitHub Actions)
技术栈:Playwright / PyInstaller / GitHub Actions / Edge TTS / Remotion

写在前面

这学期有门选修课,选修课网课 47 集、每集 25 分钟——粗算将近 20 个小时。挂机看吧,网页一休眠进度条就停;市面上的「秒刷」工具吧,原理大多是伪造multimedia/log心跳、改播放进度签名,轻则进度无效,重则账号异常。

所以我自己做了一个,思路很克制:只把「挂着」这件事自动化,「看」仍然是真的看——程序用真实浏览器一集集自然播放,服务端怎么判完成,我就怎么认。挂机是它的,知识还是我的(抽查提问时至少进度是真的)。

⚠ 边界声明(重要,先立后破)
本项目仅做「真实浏览器自然播放 → 服务端完成」:

  • 不构造/伪造/重放multimedia/log,不修改enc / attDurationEnc / videoFaceCaptureEnc / playingTime / _t等签名参数;
  • 不跳过播放、不倍速、不宣称「一键整门课秒完成」,一次 Run 只自然完成指定任务点;
  • 失败如实记录failure_stage,绝不静默伪装成功;
  • 同账号严格串行,避免多端并发被风控。
    请自行评估并遵守所在学校/平台的规则。本文只讲技术与工程,不鼓励任何滥用。

效果先上:配置一次后,每天凌晨 00:07 / 02:07 两条 GitHub cron 自动唤醒,逐章推进;或者打包成 Windows exe 双击常驻。下面按「架构 → 上手 → 核心设计 → 打包踩坑」展开。


一、整体架构

GitHub Actions cron(云端) / 本地 Xuexitong.exe(常驻循环) ↓ Scheduler 决策引擎 (RUN / NOOP / BLOCKED 熔断) ↓ TDVP 探针:逐章读服务端 DOM,刷新任务队列 ↓ Runtime:headed Playwright 真实浏览器播放 ↓ Evidence 证据 + Registry 任务账本(每章落盘) ↓ 超星服务端(isPassed = 唯一完成真源)

仓库分层很传统:app/(运行时+入口)、scheduler/(决策)、tvdp/(探针协议)、state/(持久化账本)、utils/(浏览器工厂/滑块验证码/cookie 存储)。测试 500+ 用例,分 unit / integration / regression(带真实 DOM fixture)三层。

两种运行模式共用同一套状态机:云端靠 GitHub cron 冷启动,本地 exe 靠常驻循环(活跃轮 2 分钟、空闲轮 30 分钟),状态文件格式一致,可以错峰双跑(后面讲为什么不用同步)。


二、快速上手

云端模式(Fork 三步)

# 1. Fork 仓库后配 Secretsgh secretsetCX_USER-b"手机号"gh secretsetCX_PASS-b"密码"# 2. 配置课程(Actions 页 Run workflow → action=initialize,粘课程 URL)gh workflow run run.yml-faction=initialize\-fcourse_url="https://mooc1.chaoxing.com/mycourse/studentstudy?chapterId=…&courseId=…&clazzid=…&cpi=…&enc=…&mooc2=1&hidetype=0&openc=…"# 3. 之后 cron 每天自动跑,或手动触发gh workflow run run.yml-faction=scheduler

课程 URL 必须从浏览器地址栏完整复制:clazzid要小写、openc/hidetype不能丢,否则服务端不渲染视频 iframe(血泪教训,见文末注意事项)。

本地模式(双击 exe)

.venv/Scripts/python.exe build.py# PyInstaller 打包,内置 chromium# 产物 dist/Xuexitong/:双击 Xuexitong.exe → 引导填账号 → 粘课程 URL → 常驻刷课

启动向导是「可跳过」式的:展示当前课程与进度,回车即开始;输s换课(旧课归档、进度保留);输数字调本次每轮推进章数。计划任务/管道等非交互场景自动跳过向导,行为完全一致。


三、核心设计:为什么它是「稳」的

1. 服务端真源(P0-3)——不走本地账本

自动学习系统最容易翻车的地方是账本漂移:本地记的「已完成」和服务端认的「已完成」对不上。我的原则是本地 state 只是缓存,超星服务端才是唯一真源:

  • 账号首次进入课程,bootstrap_registry_from_server从服务端 catalog 一次性材料化全部任务,progress.completed直接写成服务端已完成数;
  • 每轮执行前,TDVP 探针逐章 point-read 服务端 DOM(读.task-item > .status完成标记),reconcile 掉本地账本与真实世界的偏差;
  • 单章是否 PASS只认服务端isPassed(SERVER_CONFIRMED_PASS),UI 自检的 10/10 只是诊断参考。

这个设计还免费送了一个能力:本地 exe 和云端 GHA错峰双跑不需要同步状态——两边各自向服务端收敛,一边刷完的章,另一边下轮探针自动跳过。

2. 熔断与 cooldown——不会无限硬刷

连续失败 ≥3 次 → 课程级BLOCKED;schedule触发下每累计 4 轮自动放行一次重试(cooldown 自动复位,可配),manual触发不受限、可立即干预;恢复成功计数清零。任务级还有「冻结」机制,反复失败的视频段不会堵死队列头部。

3. 证据体系——每个 Run 可审计

每章产出evidence/chapter_x.json:10 项闭合检查、business_verdict、passed_object_ids、failure_stage。失败分类到 10 种 stage(NO_CARDS_IFRAME/PLAYBACK_STALLED/LOGIN_FAILED…),CI 日志直接打印结构化诊断。出了问题看证据,不猜。

4. 账号隔离与安全

状态按state/accounts/<CX_USER 哈希>/隔离,cookie 存.cache/不入库;CI 上缺 Secrets 直接拒绝运行(exit 2),防止引擎跑到 fork 原作者的账本上。


四、打包成 exe 的踩坑实录(本文最有干货的部分)

依赖只有playwright==1.63.0,理论上 PyInstaller 一把梭——实际踩了一路。

坑 1:playwright 自带 hook 全量收集浏览器,spec 层过滤无效

PyInstaller 没有 playwright 官方 hook,但playwright 包自己通过 entry point(pyinstaller40)注册了 hook,会把driver/package/.local-browsers/(含 271MB 的 headless shell)全部收进包。在 spec 的datas里过滤是无效的,必须在 Analysis 结果上过滤——而且.exe/.dll被归类为 BINARY 走a.binaries,两边都要滤:

a=Analysis(...)a.datas=[eforeina.datasif"chromium_headless_shell"notine[0]]a.binaries=[eforeina.binariesif"chromium_headless_shell"notine[0]]

这一刀从 835MB 砍到 564MB(所有启动点都是 headed,headless shell 纯死重;ffmpeg 保留,视频播放要用)。

坑 2:contents_directory 写在 COLLECT 上会被静默忽略

contents_directory="internal"必须写在EXE段(COLLECT 从 EXE 参数继承),写在 COLLECT 上不报错、静默回落_internal——产物能跑,布局和预期不一样,排查半天。

坑 3:冻结后sys.executable -m app.run不成立

调度器起子进程跑单章用的是sys.executable -m app.run;打包后sys.executable是 exe 自己,得改成 exe CLI 重入:

ifgetattr(sys,"frozen",False):cmd=[sys.executable,"--action","run",...]else:cmd=[sys.executable,"-m","app.run",...]

坑 4:「修 A 引入 B」——停窗守卫把子进程挂死了

双击 exe 报错后窗口闪退,我加了全局停窗守卫(input("按回车关闭窗口…"))。结果下一轮真机测试,视频明明SERVER_CONFIRMED_PASS了,进程却在main()返回后卡在等回车,被看门狗 1562s 收割,PASS被聚合成FAILED,熔断计数 +1。

教训链:子进程重入 exe 也会走同一个守卫 → 守卫必须能被子进程跳过 → 调度器派生时注入XUE_NO_EXIT_PAUSE=1,守卫检测即放行。顺带在聚合层加了防御:超时被杀、但子进程 evidence 里已是 PASS(服务端确认)→ 判成功,不污染熔断。

坑 5:Path(__file__)在冻结态落到_MEIPASS

全项目 10+ 处Path(__file__).resolve().parent.parent当仓库根,打包后全指进internal/(升级即丢)。收敛成单点:

defrepo_root()->Path:# 可写根:state/ .cache/ evidence/ifgetattr(sys,"frozen",False):returnPath(sys.executable).resolve().parentreturnPath(__file__).resolve().parent.parentdefresource_root()->Path:# 只读资源:_MEIPASS...

源码形态返回值与旧表达式逐字节等价,GHA 路径零行为变化。

坑 6:Windows 看门狗没有os.killpg

killpg是 POSIX 的;Windows 分支用taskkill /F /T /PID杀整棵进程树(不然超时只杀主进程,Chromium 泄漏一桌面)。

坑 7:chromium 内置走PLAYWRIGHT_BROWSERS_PATH=0

构建前设PLAYWRIGHT_BROWSERS_PATH=0跑playwright install chromium,浏览器落进 playwright 包内,随 PyInstaller 一起收进产物——自包含、离线可用;国内下载超时配PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright或走代理。


五、错峰双跑:本地和云端怎么共存

同账号并发会互踢会话,所以两条腿时间窗错开(云端 cron 是北京 00:07/02:07)。状态收敛前面说了——靠服务端真源,不用 git 同步、不用网盘。要手动对账也行:state/accounts/<哈希>/整目录在 exe 旁和仓库之间拷贝即可,两边 schema 一致。


六、数据与现状

  • 单测 + 集成504 passed;CI 三层工作流(test / e2 / e3)把守;
  • 产物dist/Xuexitong/564MB(内置 chromium),分发就是拷目录,升级只换Xuexitong.exe+internal/;
  • 宣传片是用 Remotion 写的(React 逐帧动画 + Edge TTS 旁白),58 秒,纯代码渲染——工程在promo/,也算这套技术栈的顺手展示。

完整操作手册(配置全表、错误速查 20+ 条、FAQ)在仓库docs/USER_MANUAL.md,打包后随 exe 附带。

总结

这个项目对我最大的价值不是「省了 20 小时」,而是把「不可靠的挂机」变成「可审计的流水线」:探针发现任务 → 真实播放 → 服务端确认 → 证据落盘 → 熔断兜底,每一环都有据可查。

仓库:https://github.com/Thy985/xuexitong—— 觉得思路有用的话点个 Star;Issue / PR 都欢迎(尤其欢迎不同平台账号的实测反馈)。

再次声明:请合理使用,遵守所在学校规定;工具只负责「真实地挂着」,学习本身没有捷径。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 2:45:06

AI新手第二天实战:从零搭建个人网站,多AI协作与避坑指南

直接输出一篇Markdown格式的博文&#xff0c;从正文内容开始。1. 第二天&#xff0c;我为什么把学习策略整个推翻了昨天是我正式啃AI的第一天&#xff0c;说实话&#xff0c;第一天我过得挺狼狈的。刷了一整晚的概念视频&#xff0c;从神经网络聊到反向传播&#xff0c;从Trans…

作者头像 李华
网站建设 2026/10/4 2:41:16

异步TCP编程实战:从事件循环到C#实现与避坑指南

简介&#xff1a;这份压缩包围绕异步TCP通信提供了一套完整的聊天程序工程&#xff0c;面向需要掌握高并发网络编程的C#开发者。内容将TCP协议的可靠传输、三次握手、滑动窗口和拥塞控制等核心机制&#xff0c;与异步事件驱动编程结合&#xff0c;清楚展示如何借助少量线程处理…

作者头像 李华
网站建设 2026/10/4 2:40:54

单细胞测序数据整合实战:Seurat与Harmony流程详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 2:40:16

Java+Vue公寓出租系统全拆解:数据库、后端、前端一网打尽

市面上这套“Java Vue 公寓出租系统”其实非常多&#xff0c;但很多朋友拿到源码之后&#xff0c;最常见的状态是&#xff1a;项目能跑起来&#xff0c;却看不懂里面每一张表为什么这么设计、每一个接口为什么这么写。等到面试官问一句“你讲讲这个项目的权限怎么做的”、“退…

作者头像 李华
网站建设 2026/10/4 2:38:55

手游内存技术分析:从懒人精灵看地址空间、跨进程读写与Hook

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 2:38:23

Linux UDP组播编程实战:从IGMP原理到Socket代码与排查

1. 为什么要用组播&#xff1a;一次真实的线上事故先讲个我几年前踩过的坑。当时在一家做视频直播的公司&#xff0c;有一套内部的流媒体分发系统&#xff0c;边缘节点之间要同步节目列表和状态信息。最开始实现的时候&#xff0c;节点之间用的是TCP点对点通信&#xff0c;每个…

作者头像 李华