## 1. 环境准备与核心思路
大家好,我是老陈,一个在自动化领域摸爬滚打了十来年的老码农。今天想和大家聊聊一个非常“接地气”的话题:如何用Python写一个能自动帮你完成雨课堂课程学习的脚本。我知道,很多朋友都遇到过类似的困扰——学校或单位要求完成大量的在线课程,内容或许不是重点,但“刷时长”这个硬性指标却让人头疼。手动挂着吧,费时费力还容易错过弹窗;不挂吧,又完不成任务。这时候,让程序来帮你“学习”,就成了一个很实际的需求。
在开始敲代码之前,我们得先搞清楚一件事:**自动化刷课的本质是什么?** 它不是真的让AI去理解课程内容,而是模拟一个真实用户在学习过程中的所有网络行为。简单来说,就是“欺骗”服务器,让它以为屏幕前一直有一个勤奋的学生在认真学习。这听起来有点取巧,但从技术学习的角度,这是一个绝佳的实战项目,能让你深入理解Web交互、会话管理和反爬虫策略。
要实现这个目标,我们主要有两条技术路线可以选。**第一条路是“浏览器模拟流”**,也就是使用像Selenium、Playwright这样的工具,直接操控一个真实的浏览器,点击、播放、答题,完全模拟人的操作。这种方法的优点是直观,所见即所得,几乎能应对所有复杂的网页交互,包括JavaScript动态加载的内容。但缺点也很明显:笨重、耗资源,运行起来需要打开浏览器界面,而且速度相对较慢。
**第二条路,也是我们今天重点要讲的,是“协议请求流”**。这条路不走视觉模拟的“面子”,而是直击网络通信的“里子”。我们通过浏览器的开发者工具(按F12就能打开),仔细分析你在正常观看视频时,浏览器和雨课堂服务器之间到底发送和接收了哪些HTTP请求。你会发现,关键的进度上报、心跳维持,其实都是通过一些特定的API接口完成的。我们的脚本只要能够模拟这些请求,以正确的频率、携带正确的数据发给服务器,就能达到“刷课”的目的。这种方法效率极高,可以在后台静默运行,不依赖图形界面,是更“专业”的做法。当然,它的难度在于需要耐心地分析网络请求,理解其参数含义。别担心,接下来我会手把手带你完成这个过程。
## 2. 核心武器:requests库与登录态维持
工欲善其事,必先利其器。我们选择“协议请求流”,那么Python中最锋利的那把“器”就是`requests`库。它让我们能够用非常简洁的代码来发送HTTP请求,无论是GET还是POST。如果你还没安装,只需要在命令行里敲一句 `pip install requests` 就搞定了。
安装好之后,我们来面对第一个,也是最重要的一个挑战:**登录状态(Session)的维持**。大家都有经验,登录一个网站后,一段时间内再访问其他页面就不用重新登录了,这是因为浏览器帮你维护了一个会话状态。对于雨课堂这类需要登录的教育平台,我们的脚本也必须先“登录”进去,拿到这个通行证。直接使用账号密码模拟登录比较复杂,可能会遇到验证码。一个更简单直接的方法是:**复用你已经通过浏览器成功登录后的Cookie**。
具体怎么做呢?首先,用你的账号密码在Chrome或Edge浏览器里正常登录雨课堂。登录成功后,在当前页面按下 **F12** 键,打开开发者工具。然后切换到 **“Application”** (应用)或 **“存储”** 标签页(不同浏览器叫法略有不同)。在左侧找到 **“Cookies”** 选项,并展开你当前访问的雨课堂域名(比如 `changjiang.yuketang.cn`)。在右侧的一堆Cookie条目中,我们需要找到最关键的两个:`csrftoken` 和 `sessionid`。把它们对应的值复制下来。
这两个字符串就是你的“临时身份证”。我们的脚本会利用它们来构建请求头,让服务器认为接下来的所有请求都来自那个已经登录的“你”。这里我强烈建议你把这两个值保存在一个单独的配置文件(比如 `config.py`)里,而不是直接硬编码在主脚本中。这样既安全,又方便更换账号。下面是一个请求头(headers)的构建示例,这是与服务器对话的“礼仪规范”,务必按照浏览器的实际发送情况来设置:
```python
import requests
# 从你的配置文件或安全的地方读取这两个值
csrftoken = "你的csrftoken值"
sessionid = "你的sessionid值"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Content-Type': 'application/json',
'Cookie': f'csrftoken={csrftoken}; sessionid={sessionid}; university_id=3078; platform_id=3',
'x-csrftoken': csrftoken,
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'university-id': '3078',
'xtbz': 'cloud'
}
```
注意看,这里除了Cookie,我们还设置了一个 `x-csrftoken` 头部,这是为了防止跨站请求伪造(CSRF)攻击的一种常见安全策略,服务器会校验这个值。`User-Agent` 是告诉服务器我们是什么浏览器在访问,用常见的Chrome标识即可。其他如 `university-id` 等字段,需要根据你实际访问的雨课堂域名(如长江雨课堂、清华雨课堂等)在开发者工具里核对确认。构建好这个headers字典后,我们后续所有的 `requests.get()` 或 `requests.post()` 调用,都会把它传进去。
## 3. 实战解析:获取课程与视频列表
拿到“通行证”之后,我们就可以开始在知识的海洋里(的目录里)遨游了。第一步,是获取你账号下有哪些课程,以及每门课程里包含了哪些视频任务。这个过程就像你去图书馆,先查总书目,再找具体某本书的章节。
首先,我们需要获取当前登录用户的唯一标识 `user_id`。这个ID在后续上报学习进度时会用到。通过观察网络请求,我们可以找到获取用户信息的接口。在原始代码中,它使用了 `https://gsscut.yuketang.cn/edu_admin/check_user_session/` 这个地址。我们用设置好headers的requests去请求它,并从返回的JSON数据中提取出`user_id`字段。
```python
user_id_url = "https://gsscut.yuketang.cn/edu_admin/check_user_session/"
id_response = requests.get(url=user_id_url, headers=headers)
try:
# 使用正则表达式或直接json解析获取user_id
user_id = re.search(r'"user_id":(.+?)}', id_response.text).group(1).strip()
except:
print("获取user_id失败,请检查网络或Cookie是否失效")
exit()
```
接下来,获取课程列表。通常,会有一个接口返回用户的所有课程信息,包括课程名、教室ID、课程签名等关键信息。在原始代码中,这个接口是 `https://gsscut.yuketang.cn/mooc-api/v1/lms/user/user-courses/`。我们请求它,并解析返回的JSON数据:
```python
get_classroom_id = "https://gsscut.yuketang.cn/mooc-api/v1/lms/user/user-courses/?status=1&page=1&no_page=1&term=latest&uv_id=3078"
classroom_id_response = requests.get(url=get_classroom_id, headers=headers)
your_courses = []
try:
data = classroom_id_response.json()
for course in data["data"]["product_list"]:
your_courses.append({
"course_name": course["course_name"],
"classroom_id": course["classroom_id"],
"course_sign": course["course_sign"],
"sku_id": course["sku_id"],
"course_id": course["course_id"]
})
except Exception as e:
print("获取课程列表失败:", e)
exit()
```
现在,`your_courses` 列表里就存放了你所有的课程信息。我们可以把它打印出来,让用户选择要刷哪一门。有了具体的课程标识(如`classroom_id`和`course_sign`)后,下一步就是获取这门课下所有的视频单元。这需要调用课程章节结构接口。原始代码中的 `get_videos_ids` 函数就完成了这个工作。它会请求一个章节列表接口,然后遍历这个复杂的嵌套JSON结构,找出所有 `leaf_type` 为视频类型的单元,并把它们的ID和名称存入一个字典。
这个过程的关键在于理解服务器返回的课程数据结构。它通常是树状的:课程->章节->小节->叶子节点(视频、作业、测验)。我们需要递归或循环地遍历,筛选出类型为视频的叶子节点。拿到视频ID列表后,我们才具备了“刷”的具体目标。
## 4. 核心心跳:模拟视频观看进度上报
这是整个脚本最核心、最精妙的部分,也是“协议请求流”的灵魂所在。在视频播放过程中,雨课堂客户端(网页或App)会以固定的时间间隔(比如每15秒)向服务器发送一个“心跳”请求。这个请求告诉服务器:“我还在看呢,而且已经看到第X秒了”。我们的脚本就是要精准地模拟这个行为。
首先,我们需要知道上报进度的接口地址。通过抓包分析,原始代码中使用了 `https://gsscut.yuketang.cn/video-log/heartbeat/` 这个URL。这是一个POST请求。那么,我们要POST什么数据过去呢?这就需要仔细分析浏览器发送的真实心跳包。你会发现,它通常是一个包含多个心跳对象的列表,每个对象记录了某一时刻的播放状态。原始代码里构建的 `heart_data` 列表,就是模拟了这种批量上报。
```python
heart_data = []
for i in range(50): # 一次上报50个心跳点,模拟看了一段时间
heart_data.append({
"i": 5, # 事件类型?需根据实际情况分析
"et": "loadeddata", # 事件类型
"p": "web", # 平台
"n": "ws",
"lob": "cloud4",
"cp": video_frame, # 当前播放进度(帧或毫秒)
"fp": 0,
"tp": 0,
"sp": 1,
"ts": str(timestap), # 时间戳
"u": int(user_id), # 用户ID
"uip": "",
"c": cid, # 课程ID
"v": int(video_id), # 视频ID
"skuid": skuid,
"classroomid": classroomid,
"cc": video_id,
"d": 4976.5, # 时长?需分析
"pg": "4512143_tkqx",
"sq": 2,
"t": "video"
})
video_frame += learning_rate # 每次上报,进度向前推进
timestap += 1000 * 15 # 时间戳增加15秒,模拟时间流逝
```
**这里有几个参数需要你特别关注并自行分析确认**:`cp`(当前进度)以什么为单位?是秒、毫秒还是帧?`learning_rate`(学习速率)设置为多少合适?`d`字段代表什么?这些值都需要你用自己的账号,在真实观看一个视频时,通过开发者工具的“网络”标签页,抓取一个真实的心跳请求,对比其中的数据来确认。**切忌直接照抄**,因为不同版本的平台,参数格式可能会有变化。
脚本会循环发送心跳,直到检测到该视频的学习进度达到100%。如何检测进度呢?通常会有另一个查询接口,比如 `https://gsscut.yuketang.cn/video-log/get_video_watch_progress/`。我们在每次发送一批心跳后,调用这个查询接口,从返回的JSON中解析出 `rate`(进度比率)或 `completed`(是否完成)字段。当 `rate` 等于1.0时,就表示这个视频“学完了”。
## 5. 防检测策略与稳定性优化
写自动化脚本,不能只考虑功能实现,还得考虑如何安全、稳定、长久地运行。服务器那边也不是吃素的,它们会有各种机制来检测异常行为。如果我们模拟得太假,比如进度上报得忽快忽慢,或者频率异常,就可能有被封禁的风险。所以,我们需要一些“反反爬虫”的策略。
**第一招,加入随机性与人性化延迟。** 一个真人看视频,不会精确地每15.000秒发送一次心跳,可能会有几秒的误差。我们可以在心跳间隔上增加一点随机扰动。同时,在视频与视频之间,脚本可以随机休眠一段时间,模拟人点击“下一节”的思考间隔。
```python
import random
import time
# 在循环中,用随机值代替固定间隔
delay = 15 + random.uniform(-2, 2) # 在13-17秒之间随机
time.sleep(delay)
# 完成一个视频后,随机等待一段时间再开始下一个
time.sleep(random.randint(5, 30))
```
**第二招,处理网络异常与服务器限流。** 网络请求不可能永远成功。原始代码中已经包含了对异常的处理,比如当服务器返回包含“anomaly”(异常)或“Expected available in”(预计可用时间)等字样的错误信息时,脚本会识别并做出应对,比如重置进度或等待指定的阻塞时间。这是非常重要的健壮性设计。我们必须用 `try...except` 块包裹核心的请求代码,捕获可能出现的超时、连接错误、状态码异常等,并设计重试逻辑。
**第三招,模拟真实的观看轨迹。** 最理想的情况是,我们能获取到视频的真实时长,然后按照一定的播放速率(比如1.5倍速)来均匀地上报进度。而不是瞬间从0%跳到100%。原始代码中的 `learning_rate` 和逐步递增的 `video_frame` 就是在模拟这种渐进的过程。你可以尝试调整这个速率,让它看起来更合理。
**第四招,日志记录与状态保存。** 一个好的脚本应该能清楚地告诉你它正在做什么,遇到了什么问题。使用 `print` 语句输出关键步骤信息。更进一步,可以考虑将已完成视频的ID记录到一个本地文件或数据库中。这样,即使脚本中途因故停止,再次运行时可以先读取这个记录,跳过已经刷完的视频,实现“断点续刷”功能。
## 6. 完整代码组装与使用指南
把前面所有的模块像拼乐高一样组合起来,就形成了我们完整的自动化脚本。它的主逻辑流程非常清晰:1. 加载配置和Cookie;2. 获取用户ID和课程列表;3. 让用户选择课程;4. 获取选中课程的所有视频ID;5. 遍历每个视频,模拟心跳上报直到完成;6. 处理异常和中断。
为了让脚本更友好,我们可以增加一些交互。比如,把课程列表漂亮地打印出来,让用户输入编号选择。甚至可以实现“全选”功能,一次性刷完所有课。原始代码的 `__main__` 部分已经给出了一个很好的范例。我在这里强烈建议你,**不要直接复制粘贴代码就运行**。最好的学习方式是:对照着我讲的思路,结合你自己在浏览器开发者工具里抓取到的真实请求和响应,一行一行地去理解、去修改、去调试。
使用脚本的步骤可以总结如下:
1. **环境准备**:确保你的电脑安装了Python 3.x,并通过 `pip install requests` 安装了依赖库。
2. **获取Cookie**:按前面讲的方法,从已登录的浏览器中提取 `csrftoken` 和 `sessionid`。
3. **修改配置**:将这两个值填入脚本中对应的变量。**务必检查其他请求头参数和接口URL是否与你访问的雨课堂平台一致**。不同学校或机构的雨课堂子域名和部分参数可能不同。
4. **首次试运行**:可以先选择一门课时较短的课程进行测试。运行脚本,观察控制台输出。它会打印出正在学习的视频名称和进度。
5. **观察与调试**:如果脚本卡住或报错,仔细阅读错误信息。最常见的错误是Cookie失效(需要重新登录获取)、接口地址或参数不对。此时,再次打开开发者工具,对比脚本发送的请求和浏览器发送的请求有何不同,是调试的关键。
6. **正式运行**:测试成功后,就可以选择需要刷的课程,让脚本在后台运行了。你可以去忙别的事情,偶尔回来看一眼日志即可。
最后,我必须强调一下技术学习的初衷。我们深入剖析这个自动化脚本,目的是为了学习Python网络编程、HTTP协议、会话管理、数据分析等宝贵的实战技能。这些技能在你未来的开发工作中会非常有用。请务必在符合相关平台使用规定和学校/单位规章制度的前提下,合理、适度地使用技术工具。技术的价值在于提高效率、解放人力,而不是用于不当的捷径。好了,关于Python自动化刷课的实战分享就到这里,如果你在实践过程中遇到了具体的坑,比如某个参数怎么也抓不对,欢迎带着你的思考和抓包数据来交流,那才是技术进步最快的时候。