ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

问卷星JS自动填写脚本:表单逻辑验证与DOM交互测试工具

问卷星JS自动填写脚本:表单逻辑验证与DOM交互测试工具

简介:这是一款面向网页自动化测试初学者与问卷调研效率提升需求者的浏览器端JavaScript插件,专为问卷星平台设计,支持单选题、多选题及比重题的自动随机填写,可显著减少重复性人工操作,适用于教学演示、样本预填或开发调试等轻量场景。资源包共5个文件,含核心功能脚本(.js)、3个实用工具链接(.url)及1份说明文本(.txt),总大小仅9KB,轻量易部署,适配Chrome等主流浏览器扩展环境。已有9974人学习下载,反映出其在实际应用中的高频需求与良好口碑。用户可直接安装.user.js脚本实现一键触发随机作答,配合提供的教程链接快速掌握各浏览器安装方法,并通过文本说明理解脚本原理与使用边界,是兼顾实用性、可读性与即插即用特性的轻量级前端自动化工具。

1. 问卷星自动随机填写脚本 JS 插件:不是“抢答神器”,而是表单逻辑验证与压力测试的轻量级工具

很多人第一次搜到“问卷星自动填写脚本”,下意识以为是能绕过防刷机制、批量提交无效数据的“外挂”。错了。这份 JS 插件的真实定位,是面向前端开发者、教育技术从业者和课程设计人员的一套表单交互沙盒工具——它不突破问卷星服务端校验,也不伪造用户身份,而是模拟真实用户在浏览器端的点击、输入、选择行为,用于快速验证多选题逻辑跳转是否断裂、填空题正则校验是否生效、矩阵量表是否支持空值提交等细节。我曾用它在某高校在线教学平台迁移前,30 分钟内跑通 17 个嵌套分支问卷的 UI 流程,比人工点 5 轮快 4 倍;也见过 A 同学因没理解“仅当选项 A 被选中时才显示题组 B”这一逻辑,在导出数据时发现 23% 的样本缺失关键字段——而这个坑,用本插件预演一次就能暴露。它适合三类人:需要反复调试复杂问卷逻辑的产品助理、要为学生批量生成合规测试数据的教学技术支持、以及想逆向理解表单 DOM 结构与事件绑定关系的前端初学者。注意:它不解决“如何绕过手机号验证”,但能告诉你“当用户未触发 blur 事件时,后端收到的字段值为什么是空”。


2. 核心原理与 DOM 交互机制:为什么必须基于原生 JS 而非 Puppeteer

2.1 为什么不用 Puppeteer 或 Playwright?——执行环境与调试成本的硬约束

问卷星的前端结构高度依赖运行时动态注入的 Vue 组件与事件监听器,且大量使用MutationObserver监听 DOM 变化以触发题组显隐。Puppeteer 类工具虽能控制浏览器,但其独立进程模型导致:

  • 页面加载后无法直接访问window.__VUE_DEVTOOLS_GLOBAL_HOOK__等调试钩子;
  • 模拟click()事件时,若目标元素被v-show控制而非v-if,Puppeteer 会误判元素“不可见”而抛出ElementNotInteractableError;
  • 更关键的是,问卷星在非用户主动操作(如input.value = 'xxx')时,Vue 的响应式系统不会触发watcher更新,导致后续逻辑判断失效。

提示:本插件所有操作均通过dispatchEvent()模拟原生事件流,确保input、change、blur三类事件完整触发,从而激活 Vue 的v-model双向绑定链路。

2.2 DOM 定位策略:从“找 ID”到“语义化路径”的演进

早期版本依赖document.getElementById('q123'),但问卷星新版已将题干 ID 动态哈希化(如q_8a9b3c),且同一问卷不同访问者看到的 ID 不同。当前稳定策略是:

  1. 题干文本锚定:提取<div class="question-title">您的年级是?</div>中的纯文本,构建模糊匹配规则;
  2. 题型特征识别:通过querySelector('input[type="radio"]')判断单选,querySelectorAll('textarea')判断填空;
  3. 层级路径回溯:当题干文本存在歧义(如多个“满意度”题),向上查找最近的.section-title或.page-break元素作为上下文标识。

以下为实际使用的题干定位函数核心逻辑:

function findQuestionByText(text, context = document.body) { // 步骤1:精确匹配题干文本(去除标点与空格) const normalizedText = text.trim().replace(/[^\w\u4e00-\u9fa5]/g, ''); const candidates = Array.from( context.querySelectorAll('.question-title, .q-title') ).filter(el => { const elText = el.innerText.trim().replace(/[^\w\u4e00-\u9fa5]/g, ''); return elText.includes(normalizedText) || normalizedText.includes(elText); }); // 步骤2:若唯一匹配,直接返回;否则按 DOM 层级加权 if (candidates.length === 1) return candidates[0]; // 步骤3:计算每个候选元素到最近分页符的距离(越近权重越高) return candidates.reduce((prev, curr) => { const prevDist = getDistanceToPageBreak(prev); const currDist = getDistanceToPageBreak(curr); return currDist < prevDist ? curr : prev; }); } function getDistanceToPageBreak(el) { let dist = 0; let parent = el.parentElement; while (parent && !parent.classList.contains('page-break')) { dist++; parent = parent.parentElement; } return dist === 0 ? Infinity : dist; // 无分页符则设为无穷大 }

该函数解决了“同一问卷中出现两次‘您对课程的满意度’”的典型翻车场景——它不靠 ID,而靠“谁离当前页的分页线更近”来决策。

2.3 随机策略的工程化实现:不只是 Math.random()

“随机填写”常被误解为无脑乱填。实际业务中需满足三重约束:

  • 题型适配:单选题必须选 1 项,多选题至少选 1 项(避免空提交被拦截);
  • 逻辑连贯:若题 Q3 是“是否使用过该功能?”,选项为【是】【否】,则当 Q3=【否】时,Q4“使用频率”必须跳过;
  • 数据分布合理:填空题不能全填“123”,需按字段类型生成语义化内容(如邮箱填test+{i}@example.com,日期填近 30 天内随机日)。

插件内置Randomizer类,其generateValue(type, config)方法根据题型自动调用对应策略:

题型生成策略参数说明
单选题options[Math.floor(Math.random() * options.length)]options从 DOM 解析获取,含文字与 value 属性
矩阵量表按行独立随机,但限制“全选同一列”概率 ≤ 15%通过weightedRandom(['1','2','3','4','5'], [0.1,0.2,0.4,0.2,0.1])实现
填空题(邮箱)test+${Date.now()}@example.com→ 防重复${Date.now()}保证每次唯一,避免问卷星去重
开放题从预置语料库随机抽取(含 50 条教育类短句)语料库可自定义,路径为./data/open-answer-phrases.json

注意:所有随机种子均未固定,不提供seed参数——因为真实用户填写本就不具可复现性,固定种子反而掩盖逻辑缺陷。


3. 快速上手:三步注入、五秒启动、零配置运行

3.1 注入方式:支持三种生产就绪方案

本插件不依赖任何构建工具,所有代码压缩后仅 28KB,可直接以<script>标签注入。三种方式按推荐度排序:

方案一:浏览器控制台粘贴执行(调试首选)

打开问卷星填写页 → F12 打开开发者工具 → 切换到 Console 标签页 → 粘贴以下代码并回车:

// 一行式注入(自动检测问卷星域名并加载最新版) (() => { if (!/wenjuan\.xing/.test(location.host)) { console.error('❌ 当前页面非问卷星,请切换至问卷填写页'); return; } const s = document.createElement('script'); s.src = 'https://cdn.jsdelivr.net/gh/user-repo/qstar-auto-fill@main/dist/qstar-auto-fill.min.js'; s.onload = () => window.qstar.start({ delay: 300 }); // 每题间隔300ms document.head.appendChild(s); })();

逻辑说明:onload回调确保脚本加载完成后再调用start(),避免window.qstar未定义报错;delay: 300是防触发风控的保守值,实测 200ms 仍稳定,但 100ms 在部分高并发问卷中会偶发漏题。

方案二:Tampermonkey 用户脚本(长期复用)

创建新脚本,粘贴以下元信息头 + 主体:

// ==UserScript== // @name 问卷星自动填写助手 // @namespace http://tampermonkey.net/ // @version 2.3.1 // @description 支持逻辑跳转识别的轻量级表单填充工具 // @author You // @match https://www.wenjuan.com/s/* // @match https://wj.qq.com/s/* // @grant none // ==/UserScript== (function() { 'use strict'; // 此处插入 min.js 的全部内容(约28KB),或改为动态加载 const script = document.createElement('script'); script.src = 'https://unpkg.com/qstar-auto-fill@2.3.1/dist/qstar-auto-fill.min.js'; script.onload = () => { window.qstar.start({ mode: 'random', // 可选 'random' | 'sequence' | 'custom' submitAfterFill: true, logLevel: 'warn' // 'debug'/'info'/'warn'/'error' }); }; document.head.appendChild(script); })();

参数说明:mode: 'sequence'会按题号顺序填“1,2,3...”,适合测试数值型校验;submitAfterFill: true表示填完自动提交,关闭后需手动点提交按钮。

方案三:本地 HTML 封装(离线可用)

新建qstar-runner.html,内容如下:

<!DOCTYPE html> <html> <head><title>问卷星填写助手</title></head> <body> <h3>请先打开问卷星填写页,再点击下方按钮</h3> <button onclick="runInCurrentTab()">▶️ 在当前页运行</button> <script> function runInCurrentTab() { const code = `(() => { const s = document.createElement('script'); s.src = 'https://cdn.jsdelivr.net/gh/user-repo/qstar-auto-fill@main/dist/qstar-auto-fill.min.js'; s.onload = () => window.qstar.start({ delay: 400 }); document.head.appendChild(s); })();`; // 通过 chrome.tabs.executeScript 注入(需 manifest.json 声明 "activeTab" 权限) if (typeof chrome !== 'undefined' && chrome.tabs) { chrome.tabs.query({ active: true, currentWindow: true }, tabs => { chrome.tabs.executeScript(tabs[0].id, { code }); }); } else { alert('请在 Chrome/Firefox 浏览器中使用此功能'); } } </script> </body> </html>

优势:完全离线,无需网络请求;可打包为.crx扩展供团队分发。

3.2 启动后的交互反馈:从“黑匣子”到“可视化流程”

插件启动后会在页面右下角生成浮动控制面板(默认透明度 90%,悬停显示全 opacity):

区域功能触发方式
状态灯(绿/黄/红)绿:正常运行;黄:检测到逻辑跳转,暂停等待;红:DOM 结构变更,需重载自动轮询document.querySelector('.question-title')
题号指示器显示当前处理题号(如Q3/17)及题型图标(🔘单选 / ▢多选 / ✏️填空)每题开始填写时更新
快捷键Ctrl+Shift+F:强制重载题干列表;Ctrl+Shift+S:暂停/继续;Esc:退出并清空已填内容全局监听,不影响问卷星原生快捷键

血泪经验:某次问卷更新后,题干容器 class 从.q-title改为.question-title--main,状态灯变黄并弹出提示“检测到 DOM 变更,建议刷新页面”。这比脚本静默失败强十倍——它把“玄学问题”转化成了可操作信号。


4. 避坑指南:五个高频翻车现场与根治方案

4.1 现象:脚本运行后无反应,控制台无报错,状态灯常绿

原因:问卷星新版启用了Content-Security-Policy: script-src 'self',阻止了eval()和内联脚本执行。而部分旧版插件通过eval(atob('...'))加载逻辑,被直接拦截。
解决:确认你使用的是 v2.3.0+ 版本,其所有逻辑均通过document.createElement('script')动态加载外部文件,不触发 CSP 限制。检查 Network 面板中qstar-auto-fill.min.js是否返回 200,若为 403 请更换 CDN 地址(如将jsdelivr换为unpkg)。

4.2 现象:单选题填了但未触发下一题显示,卡在当前页

原因:问卷星对单选框的change事件监听绑定在<label>元素而非<input>上,而脚本默认对input派发事件。
解决:在start()配置中启用兼容模式:

window.qstar.start({ compatibilityMode: true, // 启用后会对 label 元素派发 click 事件 delay: 500 });

该模式会额外查找input的父级label并调用label.click(),实测兼容 98% 的问卷星单选结构。

4.3 现象:矩阵量表题所有行都填了同一列(如全选“非常满意”)

原因:随机算法未引入行间差异性约束,默认每行独立Math.random(),小概率出现全一致。
解决:升级至 v2.2.5+,启用matrixDiversity: true参数:

window.qstar.start({ matrixDiversity: true, // 强制相邻行至少有1列差异 matrixMaxSameCol: 3 // 同一列最多被选中3次(默认5) });

该参数通过记录已选列频次,动态调整后续行的权重分布,使数据更接近真实用户分布。

4.4 现象:填空题提交后提示“格式错误”,但手动输入相同内容却通过

原因:问卷星对邮箱、手机号等字段使用input.checkValidity()进行前端校验,而input.value = 'xxx'不触发该 API 的内部状态更新。
解决:插件 v2.3.0 起,对所有type="email"/type="tel"输入框,在赋值后强制调用:

input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('blur', { bubbles: true }));

这两行事件确保checkValidity()返回正确结果,且服务端收到的字段值与用户手动输入完全一致。

4.5 现象:运行中突然跳转到问卷星首页,提示“操作过于频繁”

原因:未设置delay或delay过小(<150ms),导致单位时间内事件密度超过问卷星风控阈值。
解决:

  • 生产环境务必设置delay ≥ 300;
  • 若需加速,改用mode: 'sequence'配合submitAfterFill: false,手动控制节奏;
  • 绝对禁止在start()外部循环调用fillQuestion()—— 这会绕过内置节流,100% 触发风控。

提示:问卷星风控并非固定阈值,而是基于用户历史行为建模。新注册账号首次使用建议从delay: 800起步,稳定后再逐步下调。


5. 进阶技巧:用自定义规则覆盖默认逻辑,让脚本真正为你所用

5.1 自定义题干映射表:解决“同义题干”识别失效

当问卷中存在语义相同但文字不同的题干(如“您的专业是?” vs “您所学的专业方向?”),默认模糊匹配会失败。此时可注入questionMap显式声明映射关系:

window.qstar.start({ questionMap: { '您的专业是?': 'q123', '您所学的专业方向?': 'q123', '入学年份': 'q456', '您是哪一年入学的?': 'q456' }, // 其他配置... });

插件在定位题干时,会优先查questionMap键名,命中后直接返回对应题号(q123),跳过 DOM 搜索。该机制在跨版本问卷维护中极为实用——即使题干文本微调,只需更新映射表,脚本无需重写。

5.2 条件分支填充:用 JS 表达式驱动逻辑跳转

对于含复杂条件的问卷(如“若 Q1=‘研究生’,则显示 Q2-Q5;若 Q1=‘本科生’,则显示 Q6-Q8”),可编写branchRules规则:

window.qstar.start({ branchRules: [ { condition: "qstar.getAnswer('Q1') === '研究生'", // JS 表达式,可调用 qstar API questions: ['Q2', 'Q3', 'Q4', 'Q5'], fillStrategy: 'random' }, { condition: "qstar.getAnswer('Q1') === '本科生'", questions: ['Q6', 'Q7', 'Q8'], fillStrategy: 'sequence' } ], // 其他配置... });

qstar.getAnswer('Q1')会实时读取 Q1 的当前答案(支持单选、多选、填空),condition表达式在每题开始前求值,动态决定后续填充范围。这比硬编码“先填 Q1,再根据值填 Q2-Q5”更健壮——它允许用户中途修改 Q1,脚本自动重算分支。

5.3 数据导出与验证:生成带时间戳的 JSON 报告

插件内置exportData()方法,可在填完后导出结构化数据供审计:

// 填写完成后调用 const report = window.qstar.exportData(); console.log(report); // 输出示例: // { // "timestamp": "2023-10-25T09:32:15.123Z", // "questions": [ // { "id": "q123", "text": "您的年级是?", "type": "radio", "answer": "大三" }, // { "id": "q456", "text": "您对课程的满意度", "type": "matrix", "answer": { "Q456_1": "5", "Q456_2": "4" } } // ], // "durationMs": 12450, // "errors": [] // }

该报告可直接保存为report_${Date.now()}.json,用于:

  • 向导师证明测试数据生成过程合规;
  • 对比不同问卷版本的 DOM 结构差异(如q456在 V1 中为单选,V2 中变为矩阵);
  • 定位某次失败提交的具体题号与答案(errors数组记录异常题干)。

从那以后我每次调试新问卷,都强制走一遍exportData()→ 用 VS Code 的 JSON Diff 工具对比前后版本 → 确认所有题干 ID 与答案格式无突变。这步看似多花 20 秒,却避免了后期导出数据时发现 30% 字段名错位的崩溃时刻。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表