写在前面的话
脚本的作者不是我,首先感谢原作者和题库接口的作者,下面的脚本是我测试后加了测试随机化提交,避免服务器出问题(
当然这段时间服务器非常卡
)如果你不知道什么tampermonkey
(you猴),那就按照下面操作就可以了!有问题可以下方留言,该文章具有时效性!
谈一下原理
(可跳过!
)基于jQuery与tampermonkey实现的自动提交,数据源是http://jk.fm210.cn/
,tampermonkey主要是用来解决跨域请求问题,不然,你也可以直接注入js,调用ajax!jQuery主要是用来操作dom文档对象的,提取数据或者注入数据的!
重头戏其实也不是这个脚本,主要是数据的提供者
!再次感谢!
准备阶段
安装Firefox浏览器
下载地址:https://www.firefox.com.cn/
安装都是双击下一步即可完成安装!
接下来的操作都是在安装的firefox浏览器中进行的…
安装tampermonkey插件
下载地址:https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/
出现下面的图标以及点击该图标会出现下图面板证明安装该插件成功!
注入脚本
下面是JavaScript写的脚本
// ==UserScript==
// @name XueTangYun_latest
// @namespace Violentmonkey Scripts
// @match https://hfut.xuetangx.com/*
// @version 9.9
// @author limouren
// @grant GM_xmlhttpRequest
// @require https://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js
// ==/UserScript==
// 自定义时间参数,不推荐更改
function randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * ( maxNum - minNum + 1 ) + minNum, 10);
break;
default:
return 0;
break;
}
}
// console.log(randomNum(1,3));
let settings = {
timeout: 20e3, // 题库响应等待时间,默认20s, 不必修改
requestInterval: randomNum(2, 4)*1000, // 请求间隔,不要太快,不建议动
// 注意,考虑到每次间隔一致可能会太过明显,所以在下面间隔的基础上加了随机数,0-2 s
clickLoopInterval: randomNum(3, 5)*1000, //点击事件查询间隔,一次打一个,必须大于2s,否则极易造成请求失败,浪费资源,自动打勾默认关闭
};
// 失败重试列表
let retry = [];
// 左侧栏跳转提示
let lpanel = $('<div>', {
html: "按q查询答案",
id: "retry",
style: "position:fixed;z-index:9999;width: 200px;background-color: greenyellow;top:100px;left:0px;opacity: 0.7;color: purple;"
});
lpanel.appendTo('body');
// 右侧边栏操作提示
let rpanel = $('<div>', {
html: "提示:<br>加载完成后按 q 查询答案,按 s 开/关 自动点击(注意:默认关闭,间隔必须大于2s,是否开启自行判断,网络阻塞--页面转圈圈或出现重试提示时最好暂停)<br>" +
"<br><br>登录身份会时不时过期,导致页面跳转,网络不佳时或请求频繁时更严重<br><br>" +
"自行判断是否适用 <br><br>成功:<span></span> 个<br>自动点击:<em>关闭</em>",
style: "position:fixed;z-index:9999;width: 200px;background-color: greenyellow;top:100px;right:0px;opacity: 0.7;color: purple;"
});
rpanel.appendTo('body');
// 点击事件,每次点击都是一次请求
let clickList = [];
// 是否全部查询成功
let successNum = 0;
let allNum = 0;
let nChange = 0;
// 控制按键 q 触发的重复请求
let submit = false;
// 开启点击
let autoClick = false;
// title 页面标识
let title = $('.title').text();
// 初始化函数
function init() {
title = $('.title').text();
autoClick = false;
retry = [];
submit = false;
successNum = 0;
allNum = 0;
nChange = 0;
clickList = [];
lpanel.html('按q查询答案');
rpanel.html("提示:<br>加载完成后按 q 查询答案,按 s 开/关 自动点击(注意:默认关闭,间隔必须大于2s,是否开启自行判断,网络阻塞--页面转圈圈或出现重试提示时最好暂停)<br>" +
"<br><br>登录身份会时不时过期,导致页面跳转,网络不佳时或请求频繁时更严重<br><br>如果页面退出或者其他的意外事件,请重复进入即可,因为服务器比较卡!" +
"自行判断是否适用 <br><br>成功:<span></span> 个<br>自动点击:<em>关闭</em>");
}
// s n 按键事件
$(window).keydown(function (event) {
switch (event.key) {
case 's':
autoClick = !autoClick;
nChange++;
if (autoClick){
clickLoop();
}else{
rpanel.find('em').text("关闭");
}
return false;
case 'q':
// 查询事件
// 新页面
if ($('.title').text() != title)
init();
// 主体只需要提交一次即可,其余就是处理失败请求
if (retry.length == 0 && submit) {
alert("没有请求可以发了");
return false;
}
lpanel.html("已开始查询,长时间无反应请刷新重试,或脚本失效");
if (retry.length == 0 && !submit) {
// 主体请求
let lis = $('.paper-list>li');
// 主体页面没有加载完
if (lis.length == 0) {
lpanel.html("还没加载完,请重试");
alert("还没加载完,请重试");
return false;
}
// 加载完成后获取总长度
allNum = lis.length;
// 标志位-主体请求已发出
submit = true;
(async () => {
for (let i = 0; i < lis.length; i++) {
let question = $(lis[i]).find('span.content').text();
// 建立好每个 li 的提示信息容器
$(lis[i]).attr("id", "li" + i);
$(lis[i]).append($('<div>', {
id: "div" + i,
}));
// 查询
post(question, lis[i]);
await sleep(settings.requestInterval);
}
})();
} else {
// 失败重试
(async () => {
for (let i = 0; i < retry.length; i++) {
let question = $(retry[i]).find('span.content').text();
post(question, retry[i]);
await sleep(settings.requestInterval);
}
})();
}
}
});
// 发请求
function post(question, li) {
// 提示信息容器
let div = $(li).find('#div' + $(li).attr("id").substring(2));
GM_xmlhttpRequest({
method: 'POST',
url: 'https://xcx.fm210.cn/api/chunshuchati/api/ajax.php?type=getda',
headers: {
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
data: 'openid=ocgIw5TSj8f1JmALChj5N_mzDt3c&w=' + question,
// 超时时间 20s
timeout: settings.timeout,
onl oad: function (xhr) {
// 200 且不为空
if (xhr.status == 200 && xhr.responseText != '') {
let obj = $.parseJSON(xhr.responseText) || {};
div.html("问题:<span style='color: mediumpurple'>" + obj['tm'] + '</span><br>' + '答案:' + "<span style='color: green'>" + ' '+ obj['da'] + ' ' + '</span>');
// 点击事件入列
clickList.push(click(li, obj['da']));
successNum++;
rpanel.find('span').text(successNum);
// 去除失败查询
if (retry.indexOf(li) != -1) {
retry.splice(retry.indexOf(li), 1);
updatelist();
}
// 所有请求都成功提示
if (successNum == allNum) {
lpanel.html("查询结束,请自行检查");
lpanel1.html("OK!")
}
} else {
// 错误响应提示
div.html("<span style='color: red'>响应错误,请稍后重试</span>");
if (retry.indexOf(li) == -1) {
retry.push(li);
updatelist();
}
}
},
ontimeout: function () {
div.html("<span style='color: red'>超时,请稍后重试</span>");
if (retry.indexOf(li) == -1) {
retry.push(li);
updatelist();
}
}
});
}
// 更新左侧边栏
function updatelist() {
lpanel.html("失败 " + retry.length + "个,结束后按s重试")
retry.forEach(function (value, index, array) {
let a = $('<a>', {
html: '<br>第 ' + eval($(value).attr('id').substring(2) + '+1') + '题, 点击查看',
href: "#" + $(value).attr('id'),
});
lpanel.append(a);
})
}
// 点击事件
function click(li, answer) {
return function () {
let choices = $(li).find('.answer-info');
for (let i = 0; i < choices.length; i++) {
if ($(li).find('.type').text().replace(" ", '') == '多选') {
if (answer.indexOf($(choices[i]).text().replace(" ", '')) != -1) {
if (!$(choices[i]).find('input').prop("checked")) {
$(choices[i]).click();
return true;
}
}else{
if ($(choices[i]).find('input').prop("checked")) {
$(choices[i]).click();
return true;
}
}
} else {
// 单选和选择
if (answer == $(choices[i]).text().replace(" ", '')) {
if (!$(choices[i]).find('input').prop("checked")) {
$(choices[i]).click();
return true;
}
}
}
}
return false;
}
}
// 执行点击事件
async function clickLoop() {
rpanel.find('em').text('点击中。。。');
while (autoClick) {
let tmp = nChange;
let flag = false;
for (let i = 0; i < clickList.length; i++) {
if (clickList[i]()) {
flag = true;
break;
}
}
if (!flag) {
rpanel.find('em').text("结束,一定检查!!! 已关闭自动点击");
$(document).scrollTop(0);
autoClick=false;
alert('已经答题结束!请检查后提交!')
return false;
}
await sleep(settings.clickLoopInterval + (100*Math.random()>>0)*20 );
if (tmp!=nChange)
return false;
}
}
const sleep = (timer) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, timer);
});
};
打开这个面板添加新脚本
复制上面的脚本添加到该区域后保存
按Ctrl + S后会跳转到下面页面说明成功添加了脚本!
登录学堂云
学堂云地址:https://hfut.xuetangx.com/#/home
登录成功后打开形势与政策的学习界面,出现下图证明脚本加载成功!
下面开始作答:
按字母键q
后开始查询题库,第一次会跳转到下面的页面,点击总是允许
即可
下面是按q
以后查询数据结果渲染的页面
接下来按下字母键s
,就开始根据查询的结果自动点击答案!
最后如果没有发什么意外的话,你会收到一个弹窗说已经答完了,然后你手动检查无误后自己最后提交试卷
!记得一定要最后提交试卷!
最后的话
据我的情况来看,因为服务器实在是太卡,经常会报404
或者500
,都是服务器的问题,如果遇到退出或者跳转到其他界面,就重新点回去,或者F5刷新,刷新点回去,重复操作即可!
脚本会受到服务器的各种影响,所以有什么问题就重新试试就可以了!
最后祝大家愉快!
更新时间4月13日
今天网站又更新了,如果不想折腾直接去这个地址http://jk.fm210.cn/查题即可,
有能力的可以去看jjm函数,通过通过burpsuite抓包分析就可解决问题,晚点如果我更新了就可继续用,我也不知道截止时间是什么时候,随缘吧,上面的地址可查
更新时间4月24日
可以完成考试!
如果出现undefined,就是该题没有查到答案,需要你自己解决!
作者停止了网页端的接口支持,这个接口是我通过抓第三方app分析出来的,作者肯定服务器流量压力也大,有条件的可以支持一下作者!链接