资讯动态

问卷星自动填写脚本实战:加权随机与真人模拟

发布时间:2026/10/17 2:35:14 来源:尧图企业网站定制
简介一份针对问卷星自动填写需求的浏览器端js插件面向需要批量处理问卷、快速完成表单填写的用户。插件支持单选题、多选题、比重题等多种题型可随机生成答案并自动提交适合课堂测验、市场调研等场景。压缩包共5个文件以.user.js脚本为核心另附txt说明文档及若干url快捷链接方便用户快速了解脚本用法和扩展工具包体仅9KB轻量易部署。该资源已有9962人学习下载属于较为流行的实用工具。通过这份资源用户可直接获得可运行的自动填写脚本省去手动编写代码的麻烦同时附带安装指引与相关站点入口有助于快速在Chrome等浏览器中启用并可根据实际题型进行简单调整提高填写效率。整体上是一份免配置、开箱即用的问卷填写辅助方案。1. 问卷星自动填写脚本先搞明白它到底做了什么开发问卷的时候最耗时间的不是设计题目而是把一份问卷反复填几十遍来验证数据能不能正常入库。单选、多选、矩阵、排序、提交前的必填校验每一处都要有足够多样本的填答数据光靠手点效率太低。问卷星自动随机填写脚本就是解决这个重复劳动的网络软件它以 JS 插件的方式注入问卷页面自动识别题目类型用加权随机策略生成答案再按真人节奏一题题填完、校验、提交。适合两类人一类是做问卷系统联调的开发者另一类是需要为表单收集测试样本的运营或数据分析从业者。2. 搭建运行环境油猴脚本与三类触发方式2.1 前置条件浏览器与脚本管理器这份资源以用户脚本的形式提供本质是一个带元数据头的 JS 文件要依赖浏览器扩展中的脚本管理器来运行。安装逻辑很简单先在浏览器扩展市场里找到脚本管理器类插件习惯上大家直接叫油猴装好后新建一个用户脚本把资源里的 js 内容整体粘贴进去保存再打开问卷页脚本就会按头部规则自动注入。下面是最小可用的脚本骨架先确认环境通不通// UserScript // name 问卷星随机填写助手 // namespace local.test // version 0.9 // description 问卷联调用的自动填答脚本 // match https://your-survey-domain.com/* // run-at document-idle // grant none // /UserScript (function () { use strict; console.log([问卷助手] 初始化完成等待表单渲染); })();代码逻辑脚本管理器在页面document-idle阶段执行这段 IIFEconsole.log输出一行日志用来确认注入是否成功。参数说明match是脚本的生效范围请改成你实际面对的问卷页域名用*做通配时要谨慎范围越窄越不容易污染其他网站run-at document-idle表示等页面主文档解析完再执行避免脚本跑得太早拿不到 DOMgrant none表示不申请额外的 GM_API保持脚本在页面上下文内直接操作权限模型最简单。如果后续要跨域读取题目配置再考虑换grant GM_xmlhttpRequest。2.2 首次运行验证从控制台确认脚本注入成功问卷星的题目结构是异步渲染的直接在执行阶段找表单元素经常会扑空。常见做法是轮询等待表单容器出现或者用 MutationObserver 监听 DOM 插入。这里给一个轮询版本逻辑直观参数也好调(function () { use strict; const CONFIG { pollInterval: 500, maxWait: 15000, triggerMode: manual }; function waitForForm(callback) { const start Date.now(); const timer setInterval(() { const form document.querySelector(#questionForm); if (form) { clearInterval(timer); callback(form); } else if (Date.now() - start CONFIG.maxWait) { clearInterval(timer); console.warn([问卷助手] 等待表单超时请检查是否在问卷详情页); } }, CONFIG.pollInterval); } function createButton() { const btn document.createElement(button); btn.textContent 随机填写; btn.style.cssText position:fixed;right:20px;bottom:20px;z-index:99999;; btn.addEventListener(click, () console.log([问卷助手] 手动触发填答)); document.body.appendChild(btn); } waitForForm(() { createButton(); console.log([问卷助手] 表单已就绪初始化完成); }); })();代码逻辑waitForForm每 500 毫秒查一次#questionForm查到就把控制权交给回调查了 15 秒还没有判定为不在问卷页直接告警退出避免无意义的死循环。参数说明pollInterval是轮询间隔500 毫秒对绝大多数问卷页够用网络慢的可以调到 800maxWait是超时上限15 秒是经验值问卷星页面加载慢的时候可以放到 20 秒triggerMode目前只是配置占位真正触发方式在 2.3 节用。验证方法打开一份真实问卷页按 F12 切到 Console能看到[问卷助手] 表单已就绪初始化完成同时页面右下角出现一个悬浮按钮说明环境已经跑通。如果 Console 里一行日志都没有先回查match规则是否覆盖了当前域名再确认脚本管理器面板里的启用开关。2.3 触发方式手动按钮、自动载入与定时执行脚本跑通之后接下来要决定什么时候开始填答。三种触发方式对应三类使用场景手动按钮适合调试先看清楚页面结构再决定要不要填自动载入适合批量打开多个标签页统一开始填答定时执行适合配合问卷的发布时间到点自动跑第一份。const CONFIG { triggerMode: manual, // manual | auto | schedule scheduleTime: 2025-01-01T10:00:00, // triggerMode 为 schedule 时生效 minDelay: 1500, // 每题最短停留时间单位毫秒 maxDelay: 4000, // 每题最长停留时间单位毫秒 submitAfterDone: true // 全部填完后是否自动提交 };代码逻辑所有运行参数集中在一个配置对象里改参数不需要动逻辑代码。scheduleTime只在triggerMode为schedule时参与计算脚本内部用Date.parse(scheduleTime) - Date.now()算出等待时长。参数说明minDelay和maxDelay控制填答节奏取值参考真人阅读速度1.5 到 4 秒是常见区间不要设置成固定值否则多份问卷的填答耗时完全一致反而显得机械。submitAfterDone建议先设为false跑几轮确认填答结果正确后再开启自动提交。参数可选值作用triggerModemanual / auto / schedule决定填答启动方式scheduleTimeISO 格式时间字符串定时触发时刻minDelay / maxDelay毫秒每题停留随机区间submitAfterDonetrue / false是否自动提交问卷使用建议手动模式永远是第一选择新脚本或者新问卷模板第一次跑必须用手动触发观察一轮自动和定时模式适合确认无误后的批量场景但也不要在正式环境高频使用短时间内同 IP 大量提交很容易触发平台的异常检测这个在第 4 章还会提到。3. 随机填写逻辑拆解加权策略、时间模拟与边界参数3.1 加权随机为什么全随机反而容易翻车先解决最核心的问题答案怎么随机。初版脚本最简单粗暴的做法是Math.random()均匀随机。跑下来会发现问题均匀随机对单选和下拉框还能接受但对量表题和矩阵题答案分布会完全均匀地散在两端这种模式恰恰是机填最明显的特征。真实用户在填量表时答案会集中在中间偏积极或偏消极的区间不会每个极端值都等概率出现。所以这里引入加权随机。给每个选项配一个权重权重越大被选中的概率越高function weightedPick(options, weights) { const total weights.reduce((a, b) a b, 0); let rand Math.random() * total; for (let i 0; i options.length; i) { rand - weights[i]; if (rand 0) return options[i]; } return options[options.length - 1]; } // 示例量表题 5 个选项中间权重最大 const options [非常不同意, 不同意, 一般, 同意, 非常同意]; const weights [1, 2, 4, 2, 1]; const picked weightedPick(options, weights);代码逻辑先求权重总和生成一个0到总和的随机数然后依次减掉每个选项的权重减到小于等于0时返回当前选项。权重越大占用的区间越长命中概率越高。参数说明weights数组必须和options一一对应数量不一致会直接返回最后一个选项这点要小心权重不需要归一化写成[1, 2, 4, 2, 1]和[2, 4, 8, 4, 2]效果完全一样。实际配置时建议把量表题的权重写成两边小中间大单选和下拉框继续保持均匀随机。还要补一个边界处理当多个选项权重相同时weightedPick退化为均匀抽样这没问题但如果某个题目要求填“是/否”且业务上希望 80% 填“否”就把权重设成[1, 4]这种“有倾向的随机”比纯随机更接近真实分布。3.2 时间模拟把填答节奏伪装成真人的三个技巧随机选答案只是第一步填答节奏才是脚本像不像人的关键。早期测试阶段我踩过坑固定延迟 2 秒填一道题结果整份问卷的填答时间曲线完全水平一看就是脚本。后来改成随机区间延迟并且每道题填答前先滚动到题目位置曲线才正常。function sleep(ms) { return new Promise((resolve) setTimeout(resolve, ms)); } async function fillWithHumanPace(questions) { for (const q of questions) { const delay CONFIG.minDelay Math.random() * (CONFIG.maxDelay - CONFIG.minDelay); q.scrollIntoView({ behavior: smooth, block: center }); await sleep(delay); fillQuestion(q); } }代码逻辑fillWithHumanPace遍历题目容器先平滑滚动到题目在页面中间位置再等一个随机延迟最后执行填答。async/await串起整个过程后续想插入“填到一半暂停”或者“超过 N 秒手动接管”的逻辑都很方便。参数说明CONFIG.minDelay和CONFIG.maxDelay的差值决定了节奏变化的幅度差值太小会显得手速过于稳定差值太大又会让完成时间不可控。经验值是差值 2000 毫秒左右整卷完成时间控制在 60 到 90 秒区间。用scrollIntoView的behavior: smooth而不是直接跳转也有助于减少页面检测特征。这里有个实现细节经常被忽略scrollIntoView是异步滚动滚动动画没结束就点选个别浏览器会点不到。所以代码里把await sleep(delay)放在滚动之后、填答之前就是给滚动留出时间。3.3 题型分发与兜底处理单选、多选、矩阵、排序、填空问卷星的题型基本是定型的单选、多选、下拉、矩阵、排序、填空。脚本内部用一个分发器按题目容器特征决定走哪个处理函数。识别题型这里有一条硬经验不要依赖 class 名要用 DOM 结构和输入控件类型来判断因为 class 名会随平台版本升级变化而结构相对稳定。function fillQuestion(questionEl) { const type detectType(questionEl); switch (type) { case radio: pickRadio(questionEl); break; case checkbox: pickCheckbox(questionEl); break; case select: pickSelect(questionEl); break; case matrix: pickMatrix(questionEl); break; case sort: pickSort(questionEl); break; default: fillText(questionEl); } } function detectType(questionEl) { if (questionEl.querySelector(table)) return matrix; if (questionEl.querySelector(select)) return select; if (questionEl.querySelectorAll(input[typeradio]).length 0) return radio; if (questionEl.querySelectorAll(input[typecheckbox]).length 0) return checkbox; if (questionEl.querySelector(textarea, input[typetext])) return text; return unknown; }代码逻辑detectType按优先级判断先找有没有表格结构有就是矩阵题再找下拉框和单选、复选控件。矩阵题优先是因为它内部也包含 radio不提前拦截会被误判成普通单选。参数说明这里的分发逻辑是基础版。矩阵题要额外按行处理每一行是一个独立的 radio 组选择策略可以复用 3.1 节的weightedPick排序题则是把备选项数组做一次洗牌再按顺序依次点击。这些子函数在资源包里都是独立的直接按需替换即可。兜底逻辑是脚本稳定性的最后一道防线第一轮填完后还要有一个“补漏”环节扫描所有带必填标记的题目容器检查里面有没有已选中的控件没有就重新填一次。实现上就是遍历容器、检查checked状态把缺失的题目收集到一个数组里再重试。这个函数在 4.3 节会详细给出先记住结论无论分发逻辑多完善提交前必须有一遍全量校验。4. 避坑排查脚本失效、点选失灵与验证码暂停的五个问题这一章列出的五个问题都是实际跑问卷页时踩过的坑每条按现象、原因、解决三步写建议按顺序排查前三个问题往往一次能杀掉 80% 的故障。4.1 脚本完全没反应Console 没有任何输出现象打开问卷页Console 里连初始化日志都没有页面右下角也没有悬浮按钮。原因最常见的是match规则没覆盖当前问卷域名脚本管理器认为当前页面不在生效范围其次是脚本文件保存失败或者被管理器禁用后者概率不高但容易忽略。解决先打开脚本管理器面板确认脚本处于启用状态再把match临时改成https://*/*做一次验证能出日志说明就是匹配范围问题确认后把规则收窄回实际域名。注意通配符尽量精确到问卷服务子域避免脚本在无关页面也被注入。4.2 单选框点选无效click 没触发联动现象脚本执行后页面上某些单选题的选中态看起来是正常的但提交后数据为空或者后续跳题逻辑没有触发。原因问卷星部分题目的选项文本是包在label里的事件绑定挂在label上。脚本直接对input调用.click()时事件可能没有冒泡到外层或者平台用了自定义事件原生 click 无法触发联动。解决不要只操作input.checked改为对选项容器派发完整的鼠标事件function clickOption(inputEl) { const label inputEl.closest(label) || inputEl.parentElement; label.dispatchEvent(new MouseEvent(click, { bubbles: true, cancelable: true })); }代码逻辑先找到输入框最近的label容器再派发一个可冒泡的点击事件。bubbles: true让事件沿 DOM 树向上传播平台侧的联动监听才能收到。参数说明如果页面里没有label容器就退回到inputEl.parentElement。个别版本需要mousedown和mouseup一起派发可以在这段逻辑里加一个for循环连发三个事件保证覆盖。4.3 必填项漏填提交被拦截但脚本没报错现象脚本日志显示所有题目都填完了提交时平台仍然弹出“第 5 题未作答”且每次都不同题。原因问卷页面存在分段渲染某些题目容器是在滚动到可视区域后才加载选项的。脚本第一轮遍历时这些题目还是空壳遍历结束后才渲染出实际选项结果就是漏填。解决填答结束后追加一遍全量校验把缺失题目捞出来重填function verifyRequired(container) { const missing []; container.querySelectorAll([data-requiredtrue]).forEach((item) { const hasValue item.querySelector(input:checked) || item.querySelector(textarea:valid); if (!hasValue) { missing.push(item); } }); return missing; } const missed verifyRequired(formContainer); if (missed.length 0) { for (const item of missed) { item.scrollIntoView({ block: center }); fillQuestion(item); } const retry verifyRequired(formContainer); if (retry.length 0) { console.warn([问卷助手] 仍有必填题未填重试后自动终止); } }代码逻辑verifyRequired遍历所有带必填标记的题目容器有选中的 radio、checkbox 或有内容的 textarea 都算通过失败则滚动到该题重新填再验一遍。两次校验都失败就输出警告并终止。参数说明这里的[data-requiredtrue]是示例写法实际问卷页面的必填标记可能叫classrequired或别的属性务必先用 DevTools 检查一遍真实结构再填。这类细节属于页面结构耦合脚本发布后如果平台改版这里是最先需要更新的部分。4.4 验证码把脚本卡死填到一半停住不动现象脚本一路填到第 3 题左右突然没有后续动作了页面中间出现拖动验证码。原因问卷开启了智能验证填答行为异常时触发。验证码是 iframe 嵌入的与问卷页不同源grant none模式下脚本无法直接操作 iframe 内部元素逻辑进入死等状态。解决不要尝试自动过验证码成本高且容易被风控。脚本侧应该检测验证码容器出现然后主动暂停填答等待人工处理function detectCaptcha() { return document.querySelector(.captcha-container, #captcha, iframe[src*captcha]); } // 在每道题填答前检查一次 function checkCaptchaPause() { if (detectCaptcha()) { console.warn([问卷助手] 检测到验证码暂停填答等待人工); return new Promise((resolve) { const timer setInterval(() { if (!detectCaptcha()) { clearInterval(timer); resolve(); } }, 1000); }); } }代码逻辑checkCaptchaPause返回一个 Promise验证码在页面上时进入 1 秒一次的轮询直到人工处理完验证码消失再继续填答。参数说明detectCaptcha的选择器同样要根据实际页面调整重点是把这个检测逻辑插在每道题填答之前而不是整份问卷开头因为验证码多半是中途才弹出来的。4.5 悬浮按钮能点但提交后答案对不上现象按钮正常出现日志也显示每道题都填了但后台回收的数据里部分答案串行比如第 3 题的答案跑到了第 5 题。原因脚本按输入控件类型做了全局遍历而页面渲染时会给部分题目生成隐藏的辅助 input全局遍历把这些隐藏字段也当成有效控件取到了错误的值。解决遍历单位必须从“控件”上升到“题目容器”。每个题目容器内部再做局部查找并且只看可视控件function collectAnswers() { const answers {}; document.querySelectorAll(.question-container).forEach((q) { const qid q.dataset.questionId || q.getAttribute(data-id); const checked q.querySelector(input:checked); const textVal q.querySelector(textarea:valid); answers[qid] checked ? checked.value : (textVal ? textVal.value : 未填); }); console.table(answers); return answers; }代码逻辑通过题目容器拿到题目编号在容器范围内查选中状态保证答案归属正确。console.table输出一行一题方便对照。参数说明.question-container和>const SURVEY_CONFIGS [ { name: 登录体验问卷, urlKeyword: login-survey, minDelay: 1800, maxDelay: 3500, preferenceMap: { 您的职业: 3 } }, { name: 新版首页反馈, urlKeyword: homepage-feedback, minDelay: 2500, maxDelay: 5000, preferenceMap: {} } ]; const current SURVEY_CONFIGS.find((item) window.location.href.includes(item.urlKeyword) );代码逻辑按 URL 关键字匹配当前问卷命中后取对应配置。preferenceMap是“偏好覆盖表”用来指定某些题目固定选某个选项其余题目保持随机策略。参数说明urlKeyword取 URL 里最稳定的片段别用带参数的部分preferenceMap的键是题目文本或题目编号值是该题要固定的选项值。比如“您的职业”固定选“3”脚本检测到该题时跳过随机直接按映射填入其他题目不受影响。字段类型含义namestring问卷别名用于日志输出urlKeywordstringURL 匹配关键词minDelay / maxDelaynumber该问卷的填答节奏区间preferenceMapobject题目级固定答案映射5.2 提交前的自校验console.table 输出与必填核对脚本跑得再稳提交前也必须有一道人眼确认的关卡。我的习惯是让脚本在提交前把每道题的最终状态输出成表格手动扫一眼再决定是否放行。function validateBeforeSubmit() { const answers collectAnswers(); const emptyKeys Object.keys(answers).filter((k) answers[k] 未填); if (emptyKeys.length 0) { console.warn([问卷助手] 以下题目未填充, emptyKeys); return false; } const captchaVisible detectCaptcha(); if (captchaVisible) { console.warn([问卷助手] 验证码仍可见取消自动提交); return false; } console.table(answers); return true; }代码逻辑提交按钮被点击前先调用validateBeforeSubmit检查是否存在“未填”项、验证码是否仍然可见全部通过才执行提交。空题列表和最终答案表都会打到 Console 上。参数说明这个函数复用了 4.5 节的collectAnswers和 4.4 节的detectCaptcha三个函数配合使用覆盖了“答案归属、必填完整性、验证码阻断”三个最常见的失败场景。如果问卷里有手机号、邮箱这类格式要求再在validateBeforeSubmit里追加对应的正则校验即可。这套脚本我用下来最大的教训就是拿到任何新问卷模板先不要挂自动批处理而是打开 Console 手动触发一次填答对照console.table的输出检查题目是否正确对应。从那以后我每次拿到新的问卷资源都强制走一遍这个流程脚本翻车的概率降了大半。希望这份脚本和配置模板能帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑