蓝桥云课有效学习时长记录原理分析

本文由 @okfang616@kkkkkba 共创

0. 解决方案

首先,打开蓝桥云课官网(需要先登录
在这里插入图片描述
在学习一栏中选择题库,然后选择任意一个题目。
这里以 2. 超级余数 为例,按 F12 打开开发者工具,并切到“console”(控制台)选项卡,如下图所示:
在这里插入图片描述
紧接着复制以下代码,到控制台,然后回车执行。

setInterval(() => {
    sessionStorage.setItem("oj_learned", "1");
    console.log("oj_learned set ok!")
}, 1000)

如下图:

在这里插入图片描述
然后你这个浏览器选项卡页面就可以一直挂着了,此为最省事也是成功率最高的方法(和他记录学习时长的原理有关,感兴趣可以继续往下看

1. 起因

时间回溯到 2024年2月17日 19:22,@kkkkkba 写出了第一版代码:

// 获取具有类名为 'view-line' 的元素
var element = document.querySelector('.view-line');
// 添加20个换行符
for (var i = 0; i < 20; i++) {
    element.innerText += "\n";
}
// 保存当前状态
var Text = element.innerText;
// 定义计时器开始时间
var startTime = new Date();
// 初始化有效学习时间
var effectiveStudyTime = 0;
// 启动计时器
var timer = setInterval(function() {
    // 获取当前时间
    var currentTime = new Date();
    // 计算经过的时间(以秒为单位)
    var elapsedTime = Math.floor((currentTime - startTime) / 1000);
    // 格式化时间为 HH:mm:ss
    var hours = Math.floor(elapsedTime / 3600);
    var minutes = Math.floor((elapsedTime % 3600) / 60);
    var seconds = elapsedTime % 60;
    var formattedTime = hours.toString().padStart(2, '0') + ':' +
                        minutes.toString().padStart(2, '0') + ':' +
                        seconds.toString().padStart(2, '0');
    // 计算有效学习时间
    if (elapsedTime % 180 === 0) { // 每3分钟更新一次有效学习时间
        effectiveStudyTime++;
    }
    // 向元素内容中添加当前所计时间并换行
    element.innerText = Text + "\n当前计时器:" + formattedTime + "\n有效学习时间:" + effectiveStudyTime * 3 + "分钟";
}, 1000); // 每秒执行一次

kb 的思路很清晰:找到蓝桥云课代码编辑器的文本域DOM,定时改变其中的内容,模拟用户修改代码(假装学习

然而经过我和kb本人的实测,该方法无效。

在这里插入图片描述
如果是一年前的我,这时候就要上 selenium 了 🤣,成功率可以说是很高,但是相对来说不太好普及(由于chrome和webdriver的版本问题。会很麻烦…

所以我们把方向调转为其记录时间的原理

2. 侦察兵已上线

既然没有办法通过第一版代码的方案来实现刷分,说明蓝桥云课的防范工作也很足。
我们来一波守株待兔,打开“NetWork”选项卡,把“保留日志”钩上,然后监控 Fetch/XHRWS 两边的动静,当然同时也要一直敲击键盘。

在这里插入图片描述
大约3分钟后,第一个请求出现,6分钟后,第二个请求出现。至此,我们基本可以断定蓝桥云课是靠接口 /api/v2/problems/${problemId}/record/ 来实现的。

如果手动去调用这个接口,会不会是破局的关键点?

3. 断点一打 程序白写

我们点开 “source” (源代码) 选项卡,在 XHR/Fetch断点 里监控所有url包含 “record” 的请求
在这里插入图片描述
同样我们在假装学习了3分钟后,成功catch到了。即便代码被混淆过,我们通过调用堆栈依次向上分析,最后还是看到了核心逻辑(下图

在这里插入图片描述

function Pl(e, t) {
    var n = Object(Or.w)();
    sessionStorage.setItem("oj_learned", "0"),
    Object(Or.i)((function() {
        Al && clearInterval(Al),
        Al = setInterval((function() {
            if ("1" === sessionStorage.getItem("oj_learned")) {
                var r = e.$axios
                  , o = e.$urls;
                r.post(o.problems.reportLearningTime(t), {
                    page_ident: Object(bn.a)()
                }).then((function() {
                    n.dispatch("auth2/getStudyMinutes"),
                    sessionStorage.setItem("oj_learned", "0")
                }
                ))
            }
        }
        ), 18e4)
    }
    )),
    Object(Or.j)((function() {
        clearInterval(Al)
    }
    ))
}

相信懂 js 的小伙伴已经看出来了,setInterval一出,就是一个定时器,每隔 18e4 ms (即3分钟)运行一次。每次执行的代码内容就是去判断sessionStorageoj_learned的值是否为1,是的话就去“reportLearningTime”。report完毕后再将oj_learned设回0。

sessionStorage是HTML5中引入的一种Web Storage API,用于在客户端(浏览器)中保存临时的会话数据。它类似于cookie,但比cookie更强大和安全。
sessionStorage提供了一个简单的键值对存储系统,可以在浏览器的当前会话(即打开的标签页或浏览器窗口)中存储数据。与cookie不同的是,sessionStorage中存储的数据只在同一窗口或标签页中共享,不会被其他窗口或标签页访问到。

我们无从得知 什么条件下才能在原本的逻辑下 让 oj_learned=1,但是这已经不重要了。sessionStorage 的控制权我们也有,我们只要能保证这个定时器每次执行的时候,oj_learned的值恒为1即可破局。
从而得到了文章开始的代码:

setInterval(() => {
    sessionStorage.setItem("oj_learned", "1");
    console.log("oj_learned set ok!")
}, 1000)

4. 摆脱浏览器,拥抱脚本

如果你肯折腾的话,我们也可以把这个过程用Python脚本实现,然后你可以把脚本挂在服务器上运行。

4.1 拿到你的Cookie

我们再次回到案发现场,在控制台内输入以下代码并回车

document.cookie

在这里插入图片描述

把红圈圈住的部分存一下,4.2会用到。

4.2 执行

首先安装 requests库

pip install requests

然后复制以下代码:

import time

import requests
import json
cookie = '这里放4.1里你拿到的字符串'
while True:
    url = "https://www.lanqiao.cn/api/v2/problems/8179/record/"
    payload = json.dumps({
       "page_ident": uuid.uuid4().hex
    })
    headers = {
       'DNT': '1',
       'Cookie': cookie,
       'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
       'Content-Type': 'application/json'
    }
    response = requests.request("POST", url, headers=headers, data=payload)
    print(response.text)
    time.sleep(60 * 3 + 5)

执行即可。

4.3 kkkkkb 版代码

import configparser
import http.client
import json
import time
import uuid
import re

# 定义倒计时函数
def countdown(seconds):
    for i in range(seconds, 0, -1):
        print(f"重新请求倒计时 {i} 秒", end="\r")
        time.sleep(1)

cookie = '这里放4.1里你拿到的字符串'
problems = "8179"

print(f"cookie: {cookie}")
print(f"problems: {problems}")

# 无限循环发送 POST 请求
while True:
    conn = http.client.HTTPSConnection("www.lanqiao.cn")
    payload = json.dumps({
        "page_ident": uuid.uuid4().hex
    })
    headers = {
        'DNT': '1',
        'Cookie': cookie,
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
        'Content-Type': 'application/json'
    }
    # 发送 POST 请求
    conn.request("POST", f"/api/v2/problems/{problems}/record/", payload, headers)
    res = conn.getresponse()
    data = res.read()
    response_json = json.loads(data.decode("utf-8"))
    print(response_json)
    # 如果请求失败且返回了重试时间
    if res.status != 200 and response_json.get("code") == "limit_exceed":
        # 提取重试秒数
        retry_seconds = int(re.search(r"(\d+)秒", response_json.get("message")).group(1))
        countdown(retry_seconds)  # 进行倒计时
    elif res.status == 200:
        countdown(180)

#  '''
#  setInterval(() => {
#     sessionStorage.setItem("oj_learned", "1");
#     console.log("done");
# }, 6000)
# '''

  • 47
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值