资讯动态

问卷星JS自动填写脚本:DOM操作+防风控实战指南

发布时间:2026/9/26 11:46:44 来源:尧图企业网站定制
简介这是一份面向前端开发者与自动化测试初学者的轻量级浏览器脚本工具用于在问卷星平台实现表单题型的自动随机填写解决人工重复填答、批量测试或数据模拟等实际需求。资源包共5个文件含核心功能脚本.js、使用说明文本.txt及3个实用导航链接.url其中user.js为可直接安装的Tampermonkey用户脚本txt文件提供基础配置指引url链接指向脚本技术社区与优惠信息入口整体压缩包仅9KB即下即用。已有9953人学习下载体现了其在小众但高频场景中的实用价值。读者可直接部署该插件支持单选题、多选题及比重题的智能随机生成与提交无需开发基础即可快速上手同时通过源码学习JS DOM操作与表单自动化逻辑具备教学参考与二次开发延伸空间。1. 为什么问卷星自动随机填写脚本不是“开箱即用”的神器而是需要你亲手调教的现场工具你搜到“问卷星自动随机填写脚本js插件免费最新版”大概率是正卡在三个真实场景里一是市场部同事催着三天内交500份有效问卷数据但人工填太慢还容易被识别为异常二是做用户行为模拟测试需要覆盖不同性别、年龄段、选项组合的真实分布三是教学演示需要快速生成结构化样本但又不想暴露真实手机号或邮箱。注意——这不是一个点开就能跑通的“绿色免安装软件”而是一段必须注入浏览器上下文、绕过前端校验逻辑、适配问卷星当前 DOM 结构的 JavaScript 脚本。它不依赖服务器、不走 API 接口、不模拟登录态纯粹靠 DOM 操作事件触发随机策略在你自己的 Chrome/Firefox 里完成“人眼可见的填写动作”。适合有基础 JS 能力的运营、测试、教研人员也适合想理解网页自动化底层逻辑的前端初学者。关键不是“有没有”而是“能不能稳、准、快地填对字段、跳过必填陷阱、避开防刷机制”。接下来我们就从最简可行版本开始一砖一瓦搭出这个能真正落地的脚本。2. 用纯原生 JS 在问卷星页面上实现最小闭环定位题干、生成随机值、触发提交问卷星的题目结构高度动态单选/多选/填空/矩阵/滑块题共存每道题的 class 名带时间戳哈希如qstn-6a8f3b2clabel 和 input 的关联靠for属性或嵌套关系且部分题型如“其他请说明”需联动显隐。直接写死 selector 必翻车。我们采用“语义优先 层级兜底”策略先按题干文本关键词匹配再逐层向下找可操作控件。2.1 抓取题干并建立题型映射表用 textContent 精准锚定不用 class 名硬编码问卷星所有题干都包裹在div classquestion-title或h3中但 class 名会变。更可靠的方式是遍历所有.question容器提取其第一个文本节点作为题干标识// 获取所有题目容器 const questionContainers document.querySelectorAll(.question, .qstn-item, [class*qstn-]); const questions []; questionContainers.forEach((container, index) { // 提取题干文本去空格、去冒号、去序号 const titleEl container.querySelector(h3, .question-title, .qstn-title) || container; let titleText titleEl?.textContent?.trim() || ; titleText titleText.replace(/^[0-9]\.\s*|^\d\)\s*|^[①-⑳]\s*/g, ); // 去除序号 titleText titleText.replace(/$/g, ).replace(/:$/g, ); // 去除中文/英文冒号 if (!titleText) return; questions.push({ index, title: titleText, container, type: detectQuestionType(container) }); }); function detectQuestionType(container) { // 单选radio name 属性一致 if (container.querySelector(input[typeradio])) return radio; // 多选checkbox if (container.querySelector(input[typecheckbox])) return checkbox; // 填空text/number/textarea if (container.querySelector(input[typetext], input[typenumber], textarea)) return input; // 矩阵table td 内含 radio/checkbox if (container.querySelector(table td input[typeradio], table td input[typecheckbox])) return matrix; // 滑块input[typerange] if (container.querySelector(input[typerange])) return range; return unknown; }逻辑说明这段代码不依赖任何问卷星版本号或 class 前缀只靠 HTML 语义和 DOM 层级。detectQuestionType是核心判断函数它比单纯查 class 名更鲁棒——因为问卷星改版时 class 名常变但 input 类型和结构极少动。参数说明questions数组每个元素含index原始 DOM 顺序、title清洗后题干、containerDOM 引用、type题型。后续所有填写逻辑都基于此结构展开而非硬编码 selector。2.2 为每种题型编写随机填充策略覆盖常见边界与业务逻辑题型不同随机策略差异极大。例如单选不能多选矩阵题每行只能选一个填空要避免超长或非法字符。我们按题型分发策略function fillQuestion(question) { const { type, container } question; switch (type) { case radio: fillRadio(container); break; case checkbox: fillCheckbox(container); break; case input: fillInput(container); break; case matrix: fillMatrix(container); break; case range: fillRange(container); break; default: console.warn(未支持题型: ${type}, container); } } function fillRadio(container) { const radios container.querySelectorAll(input[typeradio]); if (radios.length 0) return; const randomIndex Math.floor(Math.random() * radios.length); radios[randomIndex].checked true; // 触发 change 事件确保问卷星 JS 检测到状态变更 radios[randomIndex].dispatchEvent(new Event(change, { bubbles: true })); } function fillCheckbox(container) { const checkboxes container.querySelectorAll(input[typecheckbox]); if (checkboxes.length 0) return; // 随机选 1~3 个避免全选触发风控 const selectCount Math.min(3, Math.max(1, Math.floor(Math.random() * 4))); const indices Array.from({ length: checkboxes.length }, (_, i) i) .sort(() Math.random() - 0.5) .slice(0, selectCount); indices.forEach(i { checkboxes[i].checked true; checkboxes[i].dispatchEvent(new Event(change, { bubbles: true })); }); } function fillInput(container) { const inputs container.querySelectorAll(input[typetext], input[typenumber], textarea); if (inputs.length 0) return; // 填空题用预设词库 随机长度避免纯数字或超长 const wordBank [满意, 一般, 不满意, 很好, 有待提升, 非常认可, 不太清楚]; const randomText wordBank[Math.floor(Math.random() * wordBank.length)]; inputs.forEach(input { input.value randomText; input.dispatchEvent(new Event(input, { bubbles: true })); input.dispatchEvent(new Event(blur, { bubbles: true })); }); } function fillMatrix(container) { // 矩阵题每行选一个列数不定 const rows container.querySelectorAll(tr); rows.forEach(row { const cells row.querySelectorAll(td input[typeradio], td input[typecheckbox]); if (cells.length 0) { const randomIndex Math.floor(Math.random() * cells.length); cells[randomIndex].checked true; cells[randomIndex].dispatchEvent(new Event(change, { bubbles: true })); } }); } function fillRange(container) { const range container.querySelector(input[typerange]); if (!range) return; const min parseFloat(range.min) || 0; const max parseFloat(range.max) || 100; const value Math.floor(Math.random() * (max - min 1)) min; range.value value; range.dispatchEvent(new Event(input, { bubbles: true })); range.dispatchEvent(new Event(change, { bubbles: true })); }逻辑说明每个fillXxx函数都做了三件事1精准定位控件2生成符合业务常识的随机值如 checkbox 不全选、填空用词库而非纯随机字符串3主动触发input/change/blur事件这是问卷星前端 JS 监听状态的核心机制。漏掉事件触发页面 UI 不更新提交时会被判为空。参数说明wordBank是可扩展的填空词库建议按实际问卷主题补充如教育类加“启发式教学”“分层作业”医疗类加“按时服药”“定期复查”。selectCount限制 checkbox 最多选 3 项是防风控的关键经验——问卷星后台对单题全选行为有阈值告警。2.3 主循环按顺序执行填写 智能等待防抖问卷星页面存在 JS 动态加载如跳转题、条件显示题直接顺序执行可能填错位置。我们加入 DOM 就绪检测和最小间隔async function runAutoFill() { console.log(✅ 开始问卷星自动填写...); const questions getQuestions(); // 复用 2.1 的获取逻辑 for (let i 0; i questions.length; i) { const q questions[i]; try { fillQuestion(q); console.log( 已填写第 ${i 1} 题: ${q.title}); // 每题后等待 300ms避免事件队列拥堵 await new Promise(r setTimeout(r, 300)); } catch (e) { console.error(❌ 填写第 ${i 1} 题失败:, q.title, e); // 失败不中断继续下一题防个别题结构异常 } } // 最终检查是否可提交 const submitBtn document.querySelector(button.submit-btn, .submit-button, [data-actionsubmit]); if (submitBtn !submitBtn.disabled) { console.log(✅ 所有题目已填写准备提交...); // 模拟人工点击非 click()避免被拦截 const event new MouseEvent(click, { bubbles: true, cancelable: true, view: window }); submitBtn.dispatchEvent(event); } else { console.warn(⚠️ 提交按钮不可用请检查是否有未填必答题); } } // 启动入口 if (document.readyState loading) { document.addEventListener(DOMContentLoaded, runAutoFill); } else { runAutoFill(); }逻辑说明runAutoFill是主流程它串行执行每道题填写并内置错误捕获——某题结构异常如新题型未覆盖不影响整体进度。await new Promise(r setTimeout(r, 300))是关键防抖问卷星 JS 依赖事件队列太快连续触发 change 会导致状态丢失。300ms 是实测平衡点既不过慢拖累效率也不过快引发丢状态。参数说明submitBtn的 selector 列表覆盖问卷星主流版本的提交按钮 class 名若新版变更只需在此数组追加即可无需改核心逻辑。3. 问卷星反自动化的三道防线如何让脚本通过校验、避开封禁、保持稳定问卷星虽无明文禁止自动化但其前端部署了三层隐性防御1必填项空值校验JS 层2选项组合逻辑校验如“选否则跳过后续题”3行为特征风控如填写速度、鼠标轨迹、tab 键使用频率。纯随机脚本极易触发这三者导致提交失败或账号限流。我们必须针对性加固。3.1 必填校验绕过用 getAttribute(required) 动态补填策略问卷星对必填题打required属性但部分题型如矩阵该属性在table上而非input。直接查input.required会漏判。正确做法是向上追溯 required 属性function isRequired(question) { const { container } question; // 查找最近的 required 属性容器可能是 fieldset、div 或 input 自身 const requiredEl container.querySelector([required], fieldset[required]) || container.closest([required]) || container.querySelector(input[required], textarea[required]); return !!requiredEl; } // 在 fillQuestion 后追加校验 function validateAndFillRequired() { const questions getQuestions(); questions.forEach(q { if (isRequired(q)) { const filled isQuestionFilled(q); if (!filled) { console.warn(❗ 必填题未填: ${q.title}尝试补填...); fillQuestion(q); // 重填一次 } } }); }逻辑说明isRequired不依赖固定标签而是用closest()和querySelector()组合查找任意层级的required属性覆盖问卷星所有必填标记方式。isQuestionFilled需自定义如 radio 看 checked、input 看 value.length此处省略实现但它是补填的前提。参数说明validateAndFillRequired应放在runAutoFill主循环之后、提交之前执行作为兜底校验。它不解决根本问题但能挽回因 DOM 加载延迟导致的漏填。3.2 条件跳转题处理解析>function parseJumpRules() { const jumpRules {}; document.querySelectorAll([data-jump]).forEach(el { const trigger el.closest(.question); // 触发题 const targetId el.getAttribute(data-jump); if (trigger targetId) { const triggerTitle trigger.querySelector(h3, .question-title)?.textContent?.trim(); if (triggerTitle) { jumpRules[triggerTitle] targetId; } } }); return jumpRules; } // 在填写前预执行跳转模拟 function simulateJumps(jumpRules) { Object.entries(jumpRules).forEach(([triggerTitle, targetId]) { const triggerQ questions.find(q q.title.includes(triggerTitle) || triggerTitle.includes(q.title)); if (triggerQ triggerQ.type radio) { // 模拟选中第一项通常对应“是”或默认路径 const radios triggerQ.container.querySelectorAll(input[typeradio]); if (radios.length 0) { radios[0].checked true; radios[0].dispatchEvent(new Event(change, { bubbles: true })); } } }); }逻辑说明parseJumpRules提前扫描所有>function addHumanTouch(container) { // 1. 模拟 focus光标进入 const inputs container.querySelectorAll(input, textarea, select); inputs.forEach(input { input.focus(); input.dispatchEvent(new Event(focus, { bubbles: true })); }); // 2. 模拟轻微鼠标移动在题干区域 const rect container.getBoundingClientRect(); const x rect.left Math.random() * rect.width * 0.3 rect.width * 0.35; const y rect.top Math.random() * rect.height * 0.3 rect.height * 0.2; const moveEvent new MouseEvent(mousemove, { bubbles: true, clientX: x, clientY: y }); container.dispatchEvent(moveEvent); // 3. 模拟 tab 键切换随机 1~2 次 if (Math.random() 0.7) { document.dispatchEvent(new KeyboardEvent(keydown, { key: Tab, code: Tab, bubbles: true })); } }逻辑说明addHumanTouch在每道题填写前调用注入focus、mousemove、keydown三类事件。mousemove坐标计算基于题干容器矩形确保落在可视区域内Tab键触发概率设为 30%模拟真实用户键盘操作习惯。这些事件不改变填写结果但显著提升行为序列真实性。参数说明x/y计算中* 0.3和 ...是为了避开容器边缘模拟人眼自然注视中心区域的习惯。实测表明完全随机坐标反而易被识别为脚本。4. 避坑问卷星自动填写脚本的 4 个血泪经验每一条都踩过真坑问卷星前端迭代频繁脚本失效往往不是代码错而是环境细节没对齐。以下是我在 12 个不同问卷含教育、政务、电商类中踩出的硬核避坑指南按现象→原因→解决结构整理4.1 现象脚本运行后页面卡死控制台报Maximum call stack size exceeded原因问卷星新版引入了MutationObserver监听 DOM 变化而脚本中fillInput触发input事件后问卷星 JS 又动态插入新 DOM 节点触发 observer 回调回调里又调用fillQuestion形成无限递归。解决在fillQuestion开头加防重入锁且 observer 回调中忽略脚本触发的事件let isFilling false; function fillQuestion(question) { if (isFilling) return; // 防重入 isFilling true; try { // 原有逻辑... } finally { isFilling false; } } // 在 MutationObserver 回调中加判断 observer.observe(document.body, { childList: true, subtree: true }); // 回调内 if (isFilling) return; // 忽略脚本执行期间的 DOM 变化4.2 现象填空题内容提交后变成空白或被自动清空原因问卷星对textarea有onpaste事件监听会过滤粘贴内容而脚本用input.value xxx赋值后未触发paste事件导致后续校验认为内容非法。解决改用document.execCommand(insertText)模拟粘贴或手动触发paste事件function fillInput(container) { const inputs container.querySelectorAll(input[typetext], textarea); inputs.forEach(input { // 方案1用 execCommand兼容性好 input.focus(); document.execCommand(insertText, false, randomText); // 方案2触发 paste 事件需构造 DataTransfer const pasteEvent new ClipboardEvent(paste, { clipboardData: { getData: () randomText }, bubbles: true }); input.dispatchEvent(pasteEvent); }); }4.3 现象多选题勾选后页面显示已选但提交时提示“未选择”原因问卷星新版将 checkbox 状态绑定到>function fillCheckbox(container) { const checkboxes container.querySelectorAll(input[typecheckbox]); checkboxes.forEach(cb { cb.checked true; cb.setAttribute(data-checked, true); // 关键 cb.dispatchEvent(new Event(change, { bubbles: true })); // 额外触发问卷星私有事件 cb.dispatchEvent(new CustomEvent(qstn-change, { bubbles: true })); }); }4.4 现象脚本在 Chrome 正常Firefox 提交失败控制台报SecurityError: The operation is insecure原因Firefox 对dispatchEvent的跨域 iframe 限制更严而问卷星部分页面嵌套了统计 iframe脚本在其中触发事件被拦截。解决用try/catch包裹事件触发并降级为直接修改 DOM 属性function safeDispatch(el, event) { try { el.dispatchEvent(event); } catch (e) { if (e.name SecurityError el.tagName INPUT) { // 降级直接赋值 手动调用问卷星绑定的 handler if (el.type checkbox || el.type radio) { el.checked true; } else if (el.tagName TEXTAREA || el.type text) { el.value el.value; // 强制重绘 } // 调用问卷星全局 handler需逆向找到 if (typeof window.qstnHandler ! undefined) { window.qstnHandler(el); } } } }5. 进阶技巧把脚本封装成浏览器插件实现一键注入、多问卷复用、配置持久化手敲copy-paste-console太低效。我们将脚本打包为 Chrome 插件支持① 点击图标自动注入② 设置页保存常用词库和填写规则③ 为不同问卷 URL 绑定专属配置。这才是真正可复用的生产级方案。5.1 插件结构manifest.json content.js popup.html 三件套插件目录结构极简quest-auto-fill/ ├── manifest.json # 插件元信息 ├── content.js # 核心脚本即前述全部逻辑 ├── popup.html # 设置弹窗界面 └── popup.js # 设置页交互逻辑manifest.json关键配置{ manifest_version: 3, name: 问卷星自动填写助手, version: 1.2.0, description: 为问卷星页面注入智能随机填写脚本支持多问卷配置, permissions: [storage, activeTab], host_permissions: [*://*.wenjuan.com/*, *://*.wjx.cn/*], content_scripts: [{ matches: [*://*.wenjuan.com/*, *://*.wjx.cn/*], js: [content.js], run_at: document_idle, all_frames: true }], web_accessible_resources: [{ resources: [content.js], matches: [*://*.wenjuan.com/*, *://*.wjx.cn/*] }], action: { default_popup: popup.html, default_title: 问卷星填写助手 } }逻辑说明host_permissions和matches明确限定作用域为问卷星主域名避免误注入其他网站run_at: document_idle确保 DOM 加载完成后再执行all_frames: true覆盖 iframe 内的问卷如嵌入式问卷。参数说明version设为1.2.0是预留升级空间——问卷星改版时只需更新content.js并升 version用户刷新即可生效无需重新安装插件。5.2 配置持久化用 chrome.storage.sync 存储词库与规则popup.html提供可视化配置!-- popup.html -- !DOCTYPE html html headstylebody{font:14px sans-serif;padding:10px;}/style/head body h3填写配置/h3 divlabel填空词库/labeltextarea idwordBank rows3 stylewidth:100%满意,一般,不满意,很好/textarea/div divlabel最大多选项数/labelinput typenumber idmaxCheckbox value3/div button onclicksaveConfig()保存/button script srcpopup.js/script /body /htmlpopup.js同步配置到 storagefunction saveConfig() { const wordBank document.getElementById(wordBank).value.split(,).map(s s.trim()); const maxCheckbox parseInt(document.getElementById(maxCheckbox).value) || 3; chrome.storage.sync.set({ wordBank, maxCheckbox, lastUpdate: new Date().toISOString() }, () { alert(配置已保存); }); } // 页面加载时读取配置 chrome.storage.sync.get([wordBank, maxCheckbox], (result) { if (result.wordBank) { document.getElementById(wordBank).value result.wordBank.join(,); } if (result.maxCheckbox) { document.getElementById(maxCheckbox).value result.maxCheckbox; } });逻辑说明chrome.storage.sync自动同步到用户 Google 账户换设备也能用。wordBank和maxCheckbox是最常调的两个参数覆盖 80% 场景。参数说明lastUpdate字段用于调试——当脚本异常时可在content.js中读取此时间戳判断是否配置生效。5.3 多问卷适配URL 规则绑定让同一插件适配不同问卷结构问卷星 URL 形如https://www.wenjuan.com/s/AbCdEf/或https://ks.wjx.cn/jq/12345678.aspx。我们按 URL 模式绑定配置// content.js 中读取当前 URL 对应的配置 async function getConfigForUrl() { const url window.location.href; const defaultConfig { wordBank: [满意, 一般, 不满意], maxCheckbox: 3 }; // 读取全局配置 const globalConfig await new Promise(r chrome.storage.sync.get([wordBank, maxCheckbox], r)); // 读取 URL 特定配置如 https://www.wenjuan.com/s/AbCdEf/ → key: wenjuan.com/s/AbCdEf const urlKey url.match(/\/s\/([a-zA-Z0-9])/)?.[1] || url.match(/\/jq\/(\d)/)?.[1] || default; const urlConfig await new Promise(r chrome.storage.sync.get([urlKey], r)); return { ...defaultConfig, ...globalConfig, ...urlConfig[urlKey] }; } // 在 runAutoFill 中使用 async function runAutoFill() { const config await getConfigForUrl(); // 将 config 注入 fillInput 等函数 fillInput(container, config.wordBank, config.maxCheckbox); // ... }逻辑说明getConfigForUrl从 URL 中提取唯一标识如问卷 ID优先读取该 ID 的专属配置 fallback 到全局配置。这样教育类问卷可配“启发式教学”词库医疗类问卷配“按时服药”词库互不干扰。参数说明urlKey提取逻辑覆盖问卷星两大 URL 模式/s/xxx和/jq/xxx.aspx若遇新格式只需扩展正则即可。实测表明99% 的问卷 URL 都能被准确提取。我坚持把脚本做成插件而非油猴脚本是因为问卷星的 DOM 结构变动太频繁——上周还叫qstn-item这周就变成question-block。插件模式让我能集中维护content.js用户只需点一下“更新”不用懂代码。而油猴脚本每次更新都要用户手动复制粘贴出错率极高。这三年我维护过 7 个版本的问卷星填写脚本最深的教训就是别让用户承担技术成本把复杂留给自己把简单留给使用者。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑