您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为问卷网量身打造的全新自动化程序
- // ==UserScript==
- // @name q11e-wjw
- // @namespace https://bbs.tampermonkey.net.cn/
- // @version 1.0.1
- // @description 为问卷网量身打造的全新自动化程序
- // @author zemelee
- // @license CC-BY-NC-4.0
- // @homepageURL https://github.com/zemelee
- // @homepageURL http://sugarblack.top
- // @match https://www.wenjuan.com/*
- // ==/UserScript==
- function clearAll() {
- localStorage.clear()
- sessionStorage.clear()
- const cookies = document.cookie.split(";");
- for (let cookie of cookies) {
- const name = cookie.split("=")[0].trim();
- document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
- }
- }
- async function sleep(delay) {
- return new Promise((resolve) => { setTimeout(resolve, delay * 1000) });
- }
- function showMessage(message, type = "error") {
- const toast = document.createElement("div");
- toast.textContent = message;
- toast.style.position = "fixed";
- toast.style.top = "20px";
- toast.style.right = "20px";
- toast.style.padding = "10px 20px";
- toast.style.backgroundColor = {
- info: "#3498db",
- success: "#2ecc71",
- warning: "#f39c12",
- error: "#e74c3c"
- }[type] || "#3498db";
- toast.style.color = "white";
- toast.style.borderRadius = "5px";
- toast.style.zIndex = "9999";
- toast.style.transition = "opacity 0.5s";
- document.body.appendChild(toast);
- setTimeout(() => {
- toast.style.opacity = "0";
- setTimeout(() => document.body.removeChild(toast), 500);
- }, 3000);
- }
- function safeExecute(fn) {
- return async function (current, ...rest) {
- try {
- await fn.call(this, current, ...rest);
- } catch (error) {
- showMessage(`第${current}题有误!`);
- }
- };
- }
- function checkAgain() {
- return Array.from(document.querySelectorAll('div')).find(el => {
- return el.textContent.trim() === '再次参与';
- });
- }
- async function clickAgain() {
- const againDiv = checkAgain()
- if (againDiv) {
- againDiv.click();
- await sleep(2);
- }
- }
- async function clickRestart() {
- const restart = Array.from(document.querySelectorAll('span')).find(el => {
- return el.textContent.trim() === '重新开始';
- });
- if (restart) {
- restart.click();
- await sleep(2);
- }
- }
- async function clickContinue() {
- const elements = document.querySelectorAll('p');
- for (let el of elements) {
- if (el.textContent.trim() === '继续答题') {
- el.click()
- }
- }
- await sleep(1);
- }
- 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];
- }
- }
- }
- function randint(a, b) {
- return Math.floor(Math.random() * (b - a + 1)) + a;
- }
- const optCountable = {
- single: "div.q-single>div.option-group>div>div>div",
- multiple: "div.q-multiple>div.ws-checkbox-group>div>div>div",
- bank: "textarea",
- scoreDefault: "div.q-score-default>div>div.icon-topic>div>div>div",
- star: "div.q-score-default>div>div.icon-topic>div>div>div",
- matrixScale: {
- qs: "div.q-matrix-scale>div>div>div.option-row-content",
- opts: "div.icon-topic-content>div.row-style"
- },
- evaluation: "div.q-evaluation > div > div.evaluation-top"
- };
- async function _single(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let _selector = optCountable.single + " .ws-radio"
- let optList = Array.from(curq.querySelectorAll(_selector))
- if (optList.length != ratios.length) {
- showMessage(`第${current}题的选项与比例长度不一致`)
- ratios = Array.from({ length: opts.length }, () => randint(1, 9));
- }
- let optIdx = singleRatio([0, optList.length - 1], ratios)
- optList[optIdx].click()
- }
- async function _multiple(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let _selector = optCountable.multiple + " .ws-checkbox"
- let opts = Array.from(curq.querySelectorAll(_selector))
- if (opts.length != ratios.length) {
- showMessage(`第${current}题的选项与比例长度不一致`)
- ratios = Array.from({ length: opts.length }, () => randint(9, 99));
- }
- let mul_list = [];
- while (mul_list.reduce((acc, curr) => acc + curr, 0) <= 0) {
- mul_list = ratios.map((item) => Math.random() < item / 100 ? 1 : 0)
- }
- for (const [index, item] of mul_list.entries()) {
- if (item == 1) {
- opts[index].click()
- }
- }
- }
- async function _blank(current, texts) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let textarea = curq.querySelector(optCountable.bank)
- textarea.value = texts[randint(0, texts.length - 1)]
- }
- async function _scoreDefault(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let _selector = optCountable.scoreDefault + " .circle-icon"
- let optList = Array.from(curq.querySelectorAll(_selector))
- if (optList.length != ratios.length) {
- showMessage(`第${current}题的选项与比例长度不一致`)
- ratios = Array.from({ length: optList.length }, () => randint(1, 9));
- }
- let optIdx = singleRatio([0, optList.length - 1], ratios)
- optList[optIdx].click()
- }
- async function _matrixScale(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let qs_selector = optCountable.matrixScale.qs
- let qs = Array.from(curq.querySelectorAll(qs_selector))
- qs.forEach((qItem, index) => {
- let optList = Array.from(qItem.querySelectorAll(optCountable.matrixScale.opts + ">div"))
- let optIdx = singleRatio([0, optList.length - 1], ratios[index])
- optList[optIdx].click()
- })
- }
- async function _star(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let _selector = "div.q-score-default>div>div.icon-topic>div>div div.symbol"
- let optList = Array.from(curq.querySelectorAll(_selector))
- if (optList.length != ratios.length) {
- showMessage(`第${current}题的选项与比例长度不一致`)
- ratios = Array.from({ length: optList.length }, () => randint(1, 9));
- }
- let optIdx = singleRatio([0, optList.length - 1], ratios)
- optList[optIdx].click()
- }
- async function _evaluation(current, ratios) {
- let curq = document.querySelector(`#question-warper > div:nth-child(${current})`)
- if (curq.style.display == "none") return
- let _selector = optCountable.evaluation + ">div"
- let optList = Array.from(curq.querySelectorAll(_selector))
- if (optList.length != ratios.length) {
- showMessage(`第${current}题的选项与比例长度不一致`)
- ratios = Array.from({ length: optList.length }, () => randint(1, 9));
- }
- let optIdx = singleRatio([0, optList.length - 1], ratios)
- optList[optIdx].click()
- }
- async function submit() {
- await new Promise((resolve) => { setTimeout(resolve, 1500); });
- document.querySelector("#answer-submit-btn").click()
- }
- async function initial() {
- let qList = document.querySelectorAll('#question-warper > div');
- qList.forEach((qItem, qIdx) => {
- const qClass = qItem.querySelector("div.question-content").getAttribute("class");
- const setTitle = (type) => {
- const titleBox = qItem.querySelector("div.q-title-box .theme-text-wrap");
- titleBox.textContent = `${qIdx + 1}. ${type}-----` + titleBox.textContent;
- };
- if (qClass.includes("single")) {
- setTitle("single");
- } else if (qClass.includes("multiple")) {
- setTitle("multiple");
- } else if (qClass.includes("bank")) {
- setTitle("blank");
- } else if (qClass.includes("evaluation")) {
- setTitle("evaluation");
- } else if (qClass.includes("q-score-default")) {
- if (qItem.querySelector(".symbol-item-wrap")) { setTitle("star"); }
- else { setTitle("scoreDefault"); }
- } else if (qClass.includes("matrix-scale")) {
- setTitle("matrixScale");
- } else {
- setTitle("unkown");
- }
- });
- await sleep(1)
- }
- (async function () {
- 'use strict';
- clearAll();
- await sleep(2);
- while (!document.querySelector("#answer-submit-btn")) {
- await sleep(2)
- if (checkAgain()) {
- clickAgain();
- }
- await sleep(2)
- location.reload(true);
- }
- const single = safeExecute(_single);
- const multiple = safeExecute(_multiple);
- const blank = safeExecute(_blank);
- const scoreDefault = safeExecute(_scoreDefault);
- const matrixScale = safeExecute(_matrixScale);
- const star = safeExecute(_star);
- const evaluation = safeExecute(_evaluation);
- clickContinue()
- clickRestart()
- initial()
- // 仅需关注这里的配置即可
- const q11es = [
- // 以下代码针对示例问卷:https://www.wenjuan.com/s/UZBZJv1Y6b
- // 第一题(单选题,应该调用single)的脚本未写,留给用户自己编辑,以熟悉此脚本的用法
- () => multiple(2, [50, 50, 50, 50]), // 多选题
- () => blank(3, ["关注公众号", "做实验的研究牲"]),// 填空题
- () => scoreDefault(4, [1, 2, 3, 4, 3]), // 评分题
- () => scoreDefault(5, [1, 2, 4, 3]), // 评分题,此题比例故意写错(对示例问卷而言),以展示脚本的容错能力
- () => matrixScale(6, [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), // 矩阵评分题
- () => scoreDefault(7, [1, 2, 3, 4, 3]),
- () => scoreDefault(8, [1, 2, 3, 4, 3]),
- () => scoreDefault(9, [1, 2, 3, 4, 3]),
- () => star(10, [1, 2, 3, 4, 3]), // 星级题
- () => evaluation(11, [1, 2, 3, 4, 3]), // 评价题
- () => scoreDefault(12, [1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 5]),
- () => matrixScale(13, [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]),
- ];
- for (let q11e of q11es) {
- await q11e();
- await sleep(1) // 等待1s后继续下一题
- }
- await sleep(2) // 等待2s后提交
- await submit();
- clearAll();
- setTimeout(location.reload(true), 2.5 * 1000)
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址