雨课堂成绩与答案查询脚本:基于接口抓包与Python轮询实现
2026/9/10 4:24:36 网站建设 项目流程

简介:一份面向教师及教学管理人员的雨课堂辅助脚本,聚焦常规评估之外的临时教学需求,用于提前查看试卷答案与成绩。压缩包内仅有一个脚本文件,格式为JavaScript,体积约3千字节,轻量便携;资源标签标注为C#语言,适合具备编程基础的读者阅读,重点参考脚本中网络请求构造、接口调用与数据解析等思路。脚本虽小,却覆盖登录认证、试卷信息获取、答案提交以及成绩查询等核心环节,并涉及数据格式处理和异常错误应对,可作为理解在线教学平台接口交互流程的迷你范例。代码风格简洁紧凑,关键步骤均有注释,便于二次修改和功能扩展。目前已有四千八百三十六人学习下载,既适合教师快速部署使用,也适合开发者学习如何通过脚本模拟用户操作。需要特别提醒,实际使用应遵守雨课堂平台规则,保障考试公平与数据安全。

1. 雨课堂查看答案脚本:真正能查的是发布后的成绩与答案

“雨课堂查看答案脚本”这个标题里,“提前查看”四个字最吸引人,也最容易让人踩坑。雨课堂的学生端成绩、试卷和答案都存在服务端接口后面,老师没有点击发布之前,同一组接口返回的 JSON 里根本没有答案字段,更不存在提前拿到数据的通道。脚本能做的不是抢时间差,而是按课程 ID 轮询“成绩是否已发布”“答案是否已发布”这两个状态,一旦状态放开,就把题目、个人作答、正确答案和得分拉下来,整理成本地文件。它适合想批量查询多个测验结果的同学,也适合要核对发布情况的助教和任课老师。

2. 先定位雨课堂网页端接口,用 curl 拿到成绩与答案的原始 JSON

雨课堂网页版是典型的单页应用,成绩列表、答题详情都通过 XHR 请求返回 JSON,再由前端渲染成页面。直接分析 HTML 会浪费很多时间,正确的做法是打开浏览器开发者工具,观察实际发出的请求,把接口地址、请求方法、参数和 Cookie 都摸清楚,再转成脚本。抓包这一步并不神秘,也不需要任何代理工具,Chrome 自带的 Network 面板已经够用。

2.1 用浏览器开发者工具找雨课堂的 XHR 请求

打开雨课堂网页端并登录,按 F12 进入开发者工具,切到 Network 面板,过滤条件选择 Fetch/XHR。然后进入课程的测验入口,手动点击一次“刷新成绩”或切换到某场考试,面板里会立刻出现一批新请求。逐个点开看 Response,找到包含自己课程名、分数、题目信息的那个接口。请求名里通常带有examscoreanswerdetail这类关键词,但不同版本可能混淆命名,最终要以 Response 内容为准。

有的脚本喜欢在 Console 里注入一段代码,让你“一键查看答案”。我一般不建议这么干,尤其是从陌生人那里拿到的代码,它可能只是在偷 Cookie,而不是在查接口。自己在 Network 面板里观察数据,比任何一键脚本都可靠。

2.2 把接口请求整理成 curl 命令作为脚本基座

在 Network 面板里右键目标请求,选择 Copy as cURL,复制出来的命令很长,因为里面带了完整的 Cookie 和追踪参数。先把它保存到文本文件里,再精简成下面这种可读形式:

curl -s 'https://yuketang.example.com/v3/exam/student_course_exam_list' \ -H 'user-agent: Mozilla/5.0' \ -H 'cookie: sessionid=填写你实际抓到的值' \ --data-raw '{"course_id": "123456", "page": 1, "page_size": 20}'

解释一下参数:-s让 curl 静默输出,不打印请求进度;-H指定请求头,Cookie 要整串放进去,不要拆开;--data-raw按原样发送 POST body,这里传的是 JSON 格式参数。course_idpage_size这些字段名以你抓到的实际请求为准,不同课程类型可能有差异。

拿到原始 JSON 后,建议再装一个jq做格式化,否则压缩成一行的大对象几乎没法读:

curl -s 'https://yuketang.example.com/v3/exam/student_course_exam_list' \ -H 'cookie: sessionid=填写你实际抓到的值' \ --data-raw '{"course_id": "123456", "page": 1, "page_size": 20}' \ | jq '.data.exam_list[] | {exam_id, name, publish_status}'

这段命令把考试列表过滤成exam_idnamepublish_status三个字段。先弄清publish_status的取值,再写脚本判断逻辑,避免对未发布的考试做无意义请求。

2.3 判断“能不能查答案”的字段,不是试卷本身

接口返回的数据结构在雨课堂不同版本里会变化,但字段含义基本一致。关键是不要把“考试发布”和“答案发布”混为一谈:

字段(示例)含义脚本该做什么
publish_status考试是否已经发布未发布直接跳过
score_release_status成绩是否对学生开放未开放不查详情
answer_released答案是否公布为 false 不拉题目
my_score当前账号的分数作为结果直接展示
question_list题目和阅卷信息仅在答案开放时解析

很多脚本跑不通,是因为直接拿publish_status当答案状态,结果详情接口返回空列表。老师可能先公布成绩再公布答案,甚至只公布分数不公布答案。发布状态要分开判断,脚本里只对已经开放的数据做请求,这样既稳定,也不会对老师端造成多余压力。

3. Python 实现雨课堂查询脚本:轮询发布状态并导出成绩与答案

把接口摸清之后,就可以写正式脚本了。这里我用 Python,因为 Python 处理 JSON 和后续导出 Excel、Markdown 都比较顺手。你也可以用 Node 或 shell 实现,但核心逻辑不变:遍历课程、遍历考试、判断发布状态、按需拉详情。

3.1 先建虚拟环境,避免依赖污染系统

直接在系统 Python 里装库其实也能跑,但换台电脑、换个 Python 版本就可能在condarequests之间打架。常见做法是给脚本建独立虚拟环境:

mkdir ykt-script && cd ykt-script python -m venv venv source venv/bin/activate # Windows 使用 venv\Scripts\activate pip install requests python-dotenv

requests负责 HTTP 请求,python-dotenv用来读取.env配置文件。为什么不直接用标准库 urllib?因为requests对 JSON、Cookie、超时和错误状态的处理更省事,脚本里要反复POST多个接口,用它能少写很多样板代码。

3.2 主脚本:遍历课程、判断发布状态、导出结果

创建ykt_query.py,写入下面这段代码。它只做三件事:先拉课程列表,再拉每门课的考试列表,最后按发布状态决定是否请求详情。

import json import os import sys import time import requests from dotenv import load_dotenv load_dotenv() BASE_URL = os.getenv("YKT_BASE_URL", "https://yuketang.example.com") COOKIE = os.getenv("YKT_COOKIE", "") HEADERS = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "cookie": COOKIE, "content-type": "application/json", } def post_json(path: str, payload: dict) -> dict: """向雨课堂接口发 POST 请求,并统一处理公共返回结构。""" resp = requests.post( f"{BASE_URL}{path}", headers=HEADERS, data=json.dumps(payload), timeout=10, ) if resp.status_code == 401: raise SystemExit("Cookie 过期,请重新登录后复制 YKT_COOKIE") resp.raise_for_status() body = resp.json() if body.get("code") != 0: raise RuntimeError(f"接口返回错误: {body.get('msg')}") return body.get("data") or {} def get_course_list() -> list: data = post_json("/v3/course/student_courses", {"page": 1, "page_size": 20}) return data.get("course_list") or [] def get_exam_list(course_id: str) -> list: data = post_json("/v3/exam/student_course_exam_list", { "course_id": course_id, "page": 1, "page_size": 50, }) return data.get("exam_list") or [] def export_exam(exam: dict) -> None: name = exam.get("name", "未命名考试") if not exam.get("score_release_status"): print(f"[{name}] 成绩未发布,跳过详情请求") return detail = post_json("/v3/exam/student_exam_detail", {"exam_id": exam["exam_id"]}) result = { "exam_id": exam["exam_id"], "name": name, "my_score": detail.get("my_score"), "answer_released": detail.get("answer_released"), "questions": [], } # 只有答案开放才拉取 question_list,否则保持空数组 if detail.get("answer_released"): result["questions"] = detail.get("question_list") or [] print(json.dumps(result, ensure_ascii=False, indent=2)) if __name__ == "__main__": if not COOKIE: raise SystemExit("缺少 YKT_COOKIE,请先在 .env 中配置") if sys.stdout and hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8") for course in get_course_list(): print(f"课程:{course.get('name')}(id={course.get('course_id')})") for exam in get_exam_list(course["course_id"]): export_exam(exam) time.sleep(1)

代码里几个关键点:

post_json把 401 提前拦下来,直接提示 Cookie 过期,而不是等到后面解析 JSON 时报 KeyError;export_exam里有两个判断,score_release_status控制是否查详情,answer_released控制是否解析题目;time.sleep(1)是低频请求保护,20 场考试也会占用一段时间,但比高频连续请求被限流强得多。如果要做定时轮询,建议把间隔放到 30 分钟以上,而不是几秒刷一次。

3.3 用 .env 管理 Cookie,不写死在代码里

脚本本身不包含任何账号信息。在ykt-script目录下创建.env

YKT_BASE_URL=https://yuketang.example.com YKT_COOKIE=sessionid=你从浏览器复制的完整cookie

运行脚本:

python ykt_query.py

参数说明:

环境变量必须说明
YKT_COOKIE登录后的完整 Cookie,从 DevTools 复制
YKT_BASE_URL接口域名,默认使用示例地址

.env千万别放进 zip 里。Cookie 等价于登录态,交给别人等于把账号交出去。分发脚本时只给.env.example,里面写占位符。

4. 雨课堂查询脚本运行报错排查:cmdlet 不认命令、Cookie 失效与编码乱码

脚本写好了,接下来出场率最高的是 Windows 上的环境问题。这里把最容易卡住的几个报错按出现频率排个序,方便直接对号入座。

4.1 报“无法将‘python’项识别为 cmdlet”时先查 PATH

打开 PowerShell 运行python ykt_query.py,最常见的报错是:

无法将“python”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

npmgitpip命令也会出现一模一样的提示,原因基本都是可执行文件所在的目录不在 PATH 里。先确认解释器到底装在哪:

where.exe python

如果提示找不到,说明没装或者没勾选加入 PATH。如果装了只是当前终端没刷新,可以在当前窗口手动追加:

$env:Path += ";$env:LocalAppData\Programs\Python\Python312;$env:LocalAppData\Programs\Python\Python312\Scripts" python --version

注意这个修改只对当前窗口有效。要永久生效,更安全的做法是打开系统环境变量面板,在 Path 里新增两个目录,而不是用setx直接覆盖,因为setx可能把原有 Path 值冲掉。

4.2 在 PowerShell 中遇到禁止运行脚本时,用 RemoteSigned

如果你是通过.ps1文件启动脚本,还会遇到另一句高频提示:

因为在此系统上禁止运行脚本

这是 PowerShell 执行策略在拦截。先看当前策略:

Get-ExecutionPolicy -List

然后给当前用户设置 RemoteSigned:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force

RemoteSigned 的含义是:本机创建的脚本可以运行,从网络下载的未签名脚本会被拒绝。这个策略比Unrestricted稳妥,适合开发机和个人电脑。如果你只是想让脚本定时自动跑,也可以不碰.ps1,直接在任务计划程序里加一个“登录时启动”的任务,运行python ykt_query.py,这样连终端窗口都不用手动开。

在 Linux 或 macOS 上做定时轮询,我更习惯用 shell 循环:

#!/usr/bin/env bash while true; do python ykt_query.py >> run.log 2>&1 sleep 1800 done

这段脚本每 30 分钟跑一次。2>&1很关键,它把标准错误也写进run.log,否则接口 401 这类异常只出现在终端上,日志文件里什么都看不到。

4.3 Cookie 过期后接口返回 401,处理比续期更重要

雨课堂的登录态不是永久的,Cookie 过期后,接口大概率返回 401,或者业务码code不为 0。很多人这时候会去找“保持登录”的取巧办法,其实没必要。重新打开网页端登录一次,从 Network 面板复制新 Cookie,替换到.env里就行。

脚本里已经处理了 401,日志里看到“Cookie 过期”就直接去更新。如果以后想改提示,把post_json里的SystemExit换成更具体的输出也可以。常见的几个错误现象和处理动作可以对照这张表:

现象最可能原因处理动作
401 UnauthorizedYKT_COOKIE 过期重新登录并复制 Cookie
404 或 400接口路径或参数调整重新抓包,更新请求字段
KeyError: 'exam_id'接口没返回考试详情先判断发布状态再取字段
中文乱码Windows 控制台默认 GBKPYTHONIOENCODING=utf-8或脚本内 reconfigure

4.4 中文乱码和 zip 解压后的乱码文件名

在 Windows 控制台运行脚本时,Python 默认输出可能撞上 GBK 编码,报UnicodeEncodeError。脚本开头已经有sys.stdout.reconfigure(encoding="utf-8"),如果还不行,就在 PowerShell 里设置:

$env:PYTHONIOENCODING = "utf-8"

至于解压.zip之后看到的乱码文件名,问题和脚本本身无关,多半是压缩包使用了非 UTF-8 编码打包。这种 zip 建议让对方用带 UTF-8 选项的打包命令重新处理,或者用 7-Zip 手动打包。

5. 雨课堂脚本 .zip 打包交付:排除凭据、固定依赖与先验清单

拿到一份“雨课堂查询脚本.zip”,第一步不是解压后直接双击,而是先看压缩包内部结构。交付别人脚本时也一样,规范的 zip 应该包含一个带有版本信息的根目录,避免解压后散落一桌面文件,同时把.env、虚拟环境和缓存目录排除在外。推荐的目录结构是:

ykt-script/ ├── .env.example ├── README.md ├── requirements.txt └── ykt_query.py

打包命令可以这样写:

cd ykt-script zip -r ykt-script.zip ykt-script \ -x "ykt-script/.env" \ -x "ykt-script/__pycache__/*" \ -x "ykt-script/venv/*"

-x是排除规则。.env里是真实 Cookie,不能给任何人;venv是本地依赖目录,又大又不通用;__pycache__是 Python 运行时生成的缓存,排除后不影响功能。这样打出来的 zip,体积小,也不会有泄露登录态的隐患。

收件方拿到压缩包后,先跑两条命令验证:

unzip -l ykt-script.zip sha256sum ykt-script.zip

unzip -l列出当前目录所有文件,检查里面有没有多余的.exe或隐藏文件,确认requirements.txt存在;sha256sum记录校验值,防止传输过程中文件损坏。确认无误后,再创建虚拟环境、按.env.example填自己的 Cookie 并运行脚本。这套流程跑通之后,剩下要调的只是轮询间隔和导出格式,不会再绕进环境问题里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询