// ==UserScript== // @name 问卷星脚本 // @author 钟天宇 // @version 2.2 // @description 详见代码中的描述;任何疑问,请加qq群咨询:427847187;我看到了一定会耐心解答的!(啊不,现在不一定了,被一些**磨灭了耐心,随缘回答2023.7.12) // @match https://www.wjx.cn/* // @namespace https://greasyfork.org/users/1079332 // @downloadURL none // ==/UserScript== localStorage.clear(); sessionStorage.clear(); console.log("Storage已清除!") var cancelBtn = document.querySelector('a.layui-layer-btn1'); if (cancelBtn) { cancelBtn.click(); } (function () { //将下面的链接替换成你的问卷链接,vm不要改成vj var url = "https://www.wjx.cn/vm/hxxt2Oe.aspx"; // 若当前页面为问卷调查完成页,重定向到目标 URL,下面这个链接不要替换 if (window.location.href.includes("https://www.wjx.cn/wjx/join/complete")) { window.location.href = url; } var opt; //在单选题的函数single()中,括号里需要写题号和每个选项的比例,比如single(1, [1,2,3])表示第1题,选A占1/(1+2+3),B占2/6,C占3/6; //单选:也可以写成百分比的形式,比如[1,20,79],毕竟百分比也是比例;选项数量和比例数量必须一致,否则会报错 //在多选题的函数multiple(),括号里需写题号和各选项选择的人数比,比如multiple(2, [50,10,100])表示第2题,选A的人有50%,选B的人有10%,选C的人有100%;、 //多选:每个选项的概率彼此独立,不需要让概率和加起来等于100, //在填空题的函数vacant()中,括号里需写题号,内容和每个内容对应的比例,比如vacant(3,[1,1],["lzm","zty"])表示第3题,填写lzm和zty的比例为1: 1 //nextPage()表示翻页 //{"1": [1, 0, 0, 0, 0],.......}表示矩阵题各个小题的比例,其中的每个小题概率含义与单选题一致 //在矩阵题的函数matrix()中,括号里需要写题号和每个选项的比例,比如matrix(4,{...})表示第4题,每个小题按照中括号里写的比例刷数据 //在单选题的函数scale()中,括号里需要写题号和每个选项的比例,比如scale(5,[1,1,2,2,6])表示第5题,A:B:C:D:E = 1:1:2:2:6(和单选题意思一致) //在滑块题的函数slide()中,括号里需写题号,以及希望分数最大最小值,slide(7,50,70)表示第7题,分数介于50和70之间 //所有输入,请在英文输入法里进行,中文和英文的很多符号是不一样的,比如----> ()() {}{} :: ,, ;; //目前脚本可以处理单选(single)、多选(multiple)、矩阵(matrix)、滑块(slide)、填空(vacant)、量表(scale)类问题,这也包括了大部分常见题型 //下面是需要修改的代码,注意注意,刷完后为了躲避检测我故意让停留了10秒再提交 //所以选完后10秒内不会有任何反应,这是正常情况!啊当然如果你等了好几十秒了都还没反应的话,可能就是报错了哈哈,这个时候你可以进群咨询哦~~~(2023-05-28) single(1, [1, 2, 3]) multiple(2, [50, 10, 100]) vacant(3, ["lzm", "zty","gq"], [1, 1, 2]) nextPage(); matrix(4, { "1": [1, 0, 0, 0, 0], "2": [1, 1, 1, 1, 1], "3": [1, 0, 0, 0, 0], "4": [1, 0, 0, 0, 0]}) scale(5, [1, 1, 2, 2, 6]) scale(6, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) slide(7,50,70) opt = single(8,[1,1]) if(opt == 1){ single(9,[1,1]) } //到此结束,下面的代码可以不用管了;你需要关注的代码就是: //①问卷链接需要替换 //②刷题逻辑 //③不要删除下面的任何代码求求了;只能改你的问卷链接和刷题逻辑的代码,其余不要删; //我希望得到你们的任何反馈,不论是好评或者差评,以帮助我更新代码!!求求了~~~(2023-05-24) //强烈建议使用chrome浏览器,edge会出现一些不可预料的“特性” //提交的时候如果出现验证报错,你需要更换wifi,或者设备,或者连接手机热点,或者挂一个全局梯子(都为切换ip),或者等几十分钟再刷(2023-05-27) //滚动到页面底端 window.scrollTo(0,document.body.scrollHeight) submit(); async function submit() { // 延迟 1 秒后点击确认按钮 await new Promise((resolve) => { setTimeout(() => { //点击提交按钮 const nextBtn = document.evaluate('//*[@id="ctlNext"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if (nextBtn) { nextBtn.click(); resolve(); } }, 10000); }); // 延迟 1 秒后点击确认按钮 await new Promise((resolve) => { setTimeout(() => { document.querySelector('.layui-layer-btn0').click(); resolve(); }, 1000); }); // 延迟 2 秒后点击验证按钮 await new Promise((resolve) => { setTimeout(() => { document.querySelector('#rectMask').click(); resolve(); }, 2000); }); // 延迟 4 秒后执行 simulateSliderVerification 函数 await new Promise((resolve) => { setTimeout(() => { simulateSliderVerification(); resolve(); }, 4000); }); } //滑动验证函数 async function simulateSliderVerification() { // 获取滑块元素 const slider = document.querySelector('#nc_1__scale_text > span'); console.log("slider",slider) // 判断滑块提示文本是否为“请按住滑块” if (slider.textContent.startsWith('请按住滑块')) { // 计算滑动距离 const width = slider.offsetWidth; // 定义触发事件选项 const eventOptions = { bubbles: true, cancelable: true }; // 创建鼠标按下事件 const dragStartEvent = new MouseEvent('mousedown', eventOptions); // 创建鼠标释放事件 const dragEndEvent = new MouseEvent('mouseup', eventOptions); ///////////////////////////////////////// const steps = 10; const stepWidth = width / steps; let currX = stepWidth / 2; // 模拟鼠标点击并保持不动 slider.dispatchEvent(dragStartEvent); // 移动一定距离 const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); for (let i = 0; i < steps; i++) { slider.dispatchEvent(new MouseEvent('mousemove', Object.assign({ clientX: currX }, eventOptions))); currX += stepWidth; await delay(50); } // 松开鼠标 slider.dispatchEvent(dragEndEvent); console.log("滑动完成") } } //下一页 function nextPage(){ document.querySelector('a.button.mainBgColor').click(); } //单选题函数 function single(current, ratio) { current = current - 1 var lists = document.querySelectorAll('.field.ui-field-contain') //该单选题的选项 var ops = lists[current].getElementsByClassName('ui-controlgroup column1')[0].children ratio = normArray(ratio) //待点击的选项,返回第几个数 var index = singleRatio([1, ops.length], ratio) ops[index - 1].click() console.log("第", current + 1, "题选择了第", index, "个选项") return index } //多选题函数 function multiple(current, ratio) { current = current - 1 var lists = document.querySelectorAll('.field.ui-field-contain') //该多选题的选项 var ops = lists[current].getElementsByClassName('ui-controlgroup column1')[0].children let mul_list = []; // 获取随机数列表 function getRandomNumberList(ratio, mul_list) { return ratio.map((item) => Math.random() < item / 100 ? 1 : 0); } //生成至少包含一个1的多选题数组 while (mul_list.reduce((acc, curr) => acc + curr, 0) <= 0) { mul_list = getRandomNumberList(ratio, mul_list); } for (const [index, item] of mul_list.entries()) { if (item == 1) { ops[index].click() console.log("第", current + 1, "题选择了第", index + 1, "个选项") } } } //矩阵题函数 function matrix(current, matrix_prob) { const xpath1 = `//*[@id="divRefTab${current}"]/tbody/tr`; const a = document.evaluate(xpath1, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); let q_num = 0; //遍历每项判断是否为题 for (let i = 0; i < a.snapshotLength; i++) { const tr = a.snapshotItem(i); if (tr.getAttribute("rowindex") !== null) { q_num++; } } const xpath2 = `//*[@id="drv${current}_1"]/td`; const b = document.evaluate(xpath2, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); // 矩阵题的选项数量 const optionCount = b.snapshotLength - 1; // 转嵌套的数组///////////////////////////////////////////////////////////// const matrix_arrays = Object.values(matrix_prob); // 遍历每个数组并归一化 const normalizedArrays = matrix_arrays.map((arr) => { return normArray(arr) }); for (let i = 1; i <= q_num; i++) { //生成[2,optionCount]之间的随机数 var opt = singleRatio([2, optionCount+1], normalizedArrays[i - 1]) var nthElement = document.querySelectorAll(`#drv${current}_${i} td`)[opt - 1]; nthElement.click() } } //量表题函数 function scale(current, ratio) { let xpath = `//*[@id="div${current}"]/div[2]/div/ul/li`; let a = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); let b = singleRatio([1, a.snapshotLength], ratio); let element = document.querySelector(`#div${current} > div.scale-div > div > ul > li:nth-child(${b})`); element.click(); console.log("第", current, "题选择了第", b, "个选项") return b } //滑块题函数 function slide(current,min,max) { var score = randint(min, max) document.querySelector(`#q${current}`).value = score console.log("第", current, "题填写了", score) } //填空题函数 function vacant(current, texts, ratio) { var text_index = singleRatio([0, texts.length - 1], ratio) document.querySelector(`#q${current}`).value = texts[text_index] console.log("第", current, "题填写了", texts[text_index]) } //数组归一化 function normArray(arr) { // 计算数组元素的总和 const sum = arr.reduce((accum, val) => accum + val, 0); // 对每个元素进行最大值归一化 return arr.map(val => val / sum); } //单选题概率函数:返回一个数字[1, 4],[0.1, 0.2, 0.3, 0.4] function singleRatio(range, ratio) { let weight = []; let sum = 0; for (let i = range[0]; i <= range[1]; i++) { sum += ratio[i - range[0]]; weight.push(sum); } const rand = Math.random() * sum; for (let i = 0; i < weight.length; i++) { if (rand < weight[i]) { return i + range[0]; } } } // 生成介于a到b之间的随机整数,包括a和b function randint(a, b) { return Math.floor(Math.random() * (b - a + 1) + a); } })();