问卷星脚本

详见代码中的描述;任何疑问,请加qq群咨询:427847187;我看到了一定会耐心解答的!

当前为 2023-05-28 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         问卷星脚本
// @author       钟天宇
// @version      2.0
// @description  详见代码中的描述;任何疑问,请加qq群咨询:427847187;我看到了一定会耐心解答的!
// @match        https://www.wjx.cn/*
// @namespace    https://greasyfork.org/users/1079332
// ==/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)、多选(mutiple)、矩阵(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], 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);
    }
})();