
简介这是一款面向问卷填写场景的浏览器端JS插件由作者Ahaochan分享核心解决问卷星中单选题、多选题、比重题等题型的自动随机填写需求适合需要批量处理问卷、测试问卷逻辑或避免重复手工操作的普通用户与网络软件爱好者。资源打包为rar压缩格式整体仅9KB共包含5个文件其中js脚本是主体功能文件txt说明文件帮助了解安装与使用要点url快捷方式则关联到相关工具站点结构简洁轻量。这款工具在CSDN已有9948人浏览学习可见其需求热度较高。下载后即可获得可直接运行的脚本插件配合浏览器脚本管理扩展便能快速调用可大幅减少人工逐题点击的繁琐操作并根据需要灵活覆盖多种常见题型兼顾效率与随机性。 做问卷测试的朋友应该都有过这种感受手里一份十几道题的问卷星链接要反复测逻辑、测选项分支、测提交校验结果手动一轮轮点下来比写代码还累。后来我直接写了一个问卷星自动随机填写脚本用JS插件的形式挂在浏览器里一键跑完一份问卷随机选题、随机填空、自动翻页提交省下来的时间全拿去干正事了。这篇就把这套东西完整拆开讲讲。这个脚本适合谁用主要是做问卷测试和表单验证的开发者、做数据采集前需要批量造数的同学以及偶尔被问卷折磨的普通用户。它解决的核心问题就一个把纯手工机械化点击变成一行脚本自动执行。全文我会从方案选型、核心代码、实操配置到问题排查一条龙讲清楚保证你照着抄就能用起来。1. 方案选型为什么是JS插件而不是F12控制台1.1 几种自动填写方案的对比在动手写脚本之前我其实试过好几种路子这里直接说结论省得你再走弯路。方案优点缺点F12控制台粘贴代码零安装快速验证页面刷新就失效多页问卷交互麻烦Python Selenium可以处理复杂逻辑环境重、启动慢、容易被检测还要装一堆驱动浏览器独立插件功能全界面友好开发成本高上传商店麻烦自己本地加载调试又繁琐油猴脚本Tampermonkey轻量、跨页面持久运行、免费需要先装一个扩展壳我最终选的是油猴脚本方案也就是题目里说的js插件免费版。理由很实在Tampermonkey 本体免费脚本改完保存立刻生效match 规则能精准控制只在问卷星域名下运行不会污染其他网站。最关键的是它天然支持等到页面加载完成后注入配合 setTimeout 和 MutationObserver 能从容应对问卷星这种动态渲染的表单。1.2 脚本运行的逻辑架构这个脚本的核心思路可以拆成三步定位题目容器、按题型执行随机策略、触发下一步动作。问卷星页面的DOM结构其实挺有规律的一个题目通常是一个div内部包含题干和若干个选项。单选题是radio输入框多选题是checkbox填空题是textarea。脚本要做的就是遍历这些节点根据题目类型执行不同的随机逻辑。随机策略这块我要多说两句。不是所有题目无脑随机就完事了比如填空题如果你随一个乱码字符串提交后后台看起来就很假再比如单选题如果总随机到同一个选项那这条路径几乎没覆盖到。所以我的策略是选项随机但尽量分散文本填写用贴近真实用户口吻的词库电话、姓名这类特殊字段用结构化模板生成。2. 核心代码实现从选择题到矩阵题的完整拆解2.1 基础框架与题目定位先给一个可以直接用的油猴脚本骨架在你自己的脚本管理器里新建脚本把下面这段粘进去// UserScript // name 问卷星自动随机填写脚本 // namespace http://tampermonkey.net/ // version 1.0 // description 自动随机填写问卷星问卷支持单选、多选、填空、矩阵题 // author yourname // match https://www.wjx.cn/* // match https://www.wjx.top/* // match https://*.wjx.cn/* // match https://*.wjx.top/* // grant none // /UserScript (function() { use strict; const CONFIG { delayMin: 600, // 最小延迟毫秒 delayMax: 1500, // 最大延迟毫秒 autoSubmit: false, // 是否自动提交 debug: true // 控制台输出调试信息 }; function log(msg) { if (CONFIG.debug) { console.log([问卷自动填写], msg); } } function randomDelay() { const range CONFIG.delayMax - CONFIG.delayMin; return CONFIG.delayMin Math.floor(Math.random() * range); } function findQuestions() { return document.querySelectorAll(div.ui-field-contain); } function processQuestion(questionDiv, index) { // 每个题目的处理逻辑后面细说 log(开始处理第 ${index 1} 题); } function main() { log(脚本启动开始扫描问卷...); const questions findQuestions(); log(共发现 ${questions.length} 道题目); questions.forEach(processQuestion); // 处理完题目后再处理翻页和提交 } // 等待页面数据完全渲染 window.addEventListener(load, () { setTimeout(main, 500); }); })();这个骨架里有几个细节值得注意。match我同时写了www.wjx.cn和www.wjx.top两个域名因为问卷星的正式环境和测试环境经常换着用少写一个脚本就静默失效。每次操作之间用随机延迟而不是立刻执行是为了模拟真人答题的节奏避免短时间大量请求触发风控。2.2 单选题、多选题与填空题的随机策略接下来是重头戏把题目处理函数补全。问卷星一个题目的典型结构是这样的题干在div.field-title里选项是div.ui-control-group下的label.ui-checkbox、label.ui-radio或类似的类名。要注意新版问卷星改过几次类名所以我加了多种选择器兜底。function randomChoice(questionDiv, selector) { const options Array.from(questionDiv.querySelectorAll(selector)); if (options.length 0) return; // 去掉“其他”这类特殊选项 const normalOptions options.filter(opt { const text opt.textContent.trim(); return !text.includes(其他) !text.includes(请注明); }); const target normalOptions[Math.floor(Math.random() * normalOptions.length)]; target.click(); // 特殊处理“其他”选项弹出输入框的情况 const otherOption options.find(opt opt.textContent.includes(其他)); if (otherOption Math.random() 0.1 otherOption ! target) { otherOption.click(); const input otherOption.querySelector(input[typetext]) || questionDiv.querySelector(.other-input); if (input) { input.value randomText(2); // 自定义随机文本函数 input.dispatchEvent(new Event(input, { bubbles: true })); } } }单选题和单选题本质是一样的只是选择器不同。function processQuestion(questionDiv, index) { const title questionDiv.querySelector(.field-title, .topichtml, .topic_title)?.textContent.trim() || ; log(第 ${index 1} 题${title}); // 判断题型 const radios questionDiv.querySelectorAll(input[typeradio]); const checkboxes questionDiv.querySelectorAll(input[typecheckbox]); const textareas questionDiv.querySelectorAll(textarea); const textInputs questionDiv.querySelectorAll(input[typetext], input:not([type])); if (radios.length 0) { // 单选题 randomChoice(questionDiv, label.ui-radio, label.radio, .ui-controlgroup label); } else if (checkboxes.length 0) { // 多选题随机选2~4个 const options Array.from(questionDiv.querySelectorAll(label.ui-checkbox, label.checkbox, .ui-controlgroup label)) .filter(opt !opt.textContent.includes(其他)); if (options.length 0) return; const count Math.min( options.length, 2 Math.floor(Math.random() * Math.min(3, options.length - 1)) ); // 洗牌后取前count个 const shuffled options.sort(() Math.random() - 0.5); shuffled.slice(0, count).forEach(opt { opt.click(); // 展开“其他”输入 if (opt.textContent.includes(其他)) { const input questionDiv.querySelector(.other-input); if (input) input.value randomText(3); } }); } else if (textareas.length 0) { textareas.forEach(textarea { textarea.value randomTextByTitle(title); textarea.dispatchEvent(new Event(input, { bubbles: true })); }); } else if (textInputs.length 0) { fillTextInputs(textInputs, title); } else { // 可能是矩阵题或量表题 handleMatrixQuestion(questionDiv); } }这里有个坑我得特别提醒直接设置input.value后很多现代框架包括问卷星的Vue/React组件是感知不到变化的必须派发input事件否则表单校验会提示“未填写”。我dispatch事件时用了bubbles: true让事件能冒泡到框架的监听层。2.3 矩阵题与量表题的特殊处理矩阵题在问卷星里的结构是表格每行一个tr每一行下方的td里是一组单选按钮。这种题目如果还按普通的 radio 去随机容易出现同一行选被隐藏、表单仍提示未答的情况。稳妥的办法是逐行定位确保每行都选中一个。function handleMatrixQuestion(questionDiv) { const rows questionDiv.querySelectorAll(table tr:not(:first-child)); rows.forEach(row { const radios row.querySelectorAll(input[typeradio]); if (radios.length 0) { const target radios[Math.floor(Math.random() * radios.length)]; target.click(); } }); // 量表题如果没有table结构一般是用div.row包裹 const scaleRows questionDiv.querySelectorAll(.ui-scale-row, .jqRadio); scaleRows.forEach(row { const labels row.querySelectorAll(label); if (labels.length 0) { labels[Math.floor(Math.random() * labels.length)].click(); } }); }矩阵题容易翻车的点在于有些行是分组标题行里面没有选项需要跳过另外如果有required星标的题忘了处理提交时会弹窗提示中断整个自动流程。所以我建议在主流程里加个校验function validateAllAnswered() { const unanswered []; document.querySelectorAll(div.ui-field-contain).forEach((div, idx) { const required div.querySelector(.field-title .must, .need, .star) ! null; if (!required) return; const radioChecked div.querySelector(input[typeradio]:checked) ! null; const checkboxChecked div.querySelectorAll(input[typecheckbox]:checked).length 0; const textFilled [...div.querySelectorAll(textarea)].every(t t.value.trim() ! ); const textInputFilled [...div.querySelectorAll(input[typetext])].every(t t.value.trim() ! ); if (!radioChecked !checkboxChecked !textFilled !textInputFilled) { unanswered.push(idx 1); } }); return unanswered; }这个函数返回所有未填写的必答题编号我在提交前调用有遗漏就先补上而不是傻乎乎直接点提交。这个方法在问卷跳题逻辑多的时候尤其有用。3. 实操配置从安装到平滑运行3.1 油猴扩展的安装与脚本写入使用这个脚本前你需要先给浏览器装一个用户脚本管理器。Chrome/Edge 推荐 TampermonkeyFirefox 就装 Greasemonkey 或者 Violentmonkey都行。装好扩展后点浏览器右上角的扩展图标选择“添加新脚本”把第2小节的完整代码粘进去CtrlS 保存。保存完建议先不要直接去问卷页测试先看脚本管理器的页面右上角有没有报错图标。很多人卡在这一步脚本没生效但不知道去哪看错误。油猴的好处是它有独立的控制台入口点击弹窗里的“调试”或者按 F12 打开浏览器控制台都能看到脚本日志注意别找错控制台层级。3.2 参数调节与运行验证脚本里的 CONFIG 对象就是给你做个性化配置的。我实测下来比较合理的参数组合是这样参数推荐值说明delayMin600太快容易触发反作弊太慢浪费时间delayMax1500与上面配合形成随机节奏autoSubmitfalse手动确认后再提交防止选项异常debugtrue首次运行建议保持开启便于追踪问题把参数填好保存打开一份问卷链接等页面加载完留意控制台输出。正常情况下你应该看到类似这样的日志[问卷自动填写] 脚本启动开始扫描问卷... [问卷自动填写] 共发现 12 道题目 [问卷自动填写] 开始处理第 1 题 [问卷自动填写] 开始处理第 2 题 ...看到这些日志后滚到页面底部检查一遍选项是否都填到位。我这里建议你第一次跑的时候盯着屏幕看别做别的事因为脚本虽然会根据必填校验补题但万一问卷星改了DOM结构漏填是常事及时发现问题能省很多事。3.3 多页问卷的处理与“上一页”优化问卷星的问卷经常分多页热词里提到的“问卷星如果增加上一页”就是指这种场景。如果你打开的是分页问卷脚本只跑第一页点“下一页”后再也没反应那就白干了。要让脚本在多页问卷里持续生效需要监听页面变化并重新触发填充逻辑。function setupPageObserver() { const observer new MutationObserver(() { // 当页面内容变化时重新扫描但要防抖 clearTimeout(window.__pageScanTimer); window.__pageScanTimer setTimeout(() { if (document.querySelector(.next, .btn-next) ! null) { log(检测到分页变化重新处理当前页); const questions findQuestions(); questions.forEach(processQuestion); } }, 300); }); observer.observe(document.body, { childList: true, subtree: true }); }这里的关键是防抖。如果不禁抖页面结构一变化就扫描一次会导致同一题目被重复点击选项被反复切换。延迟300ms处理正好等动态渲染稳定下来。另外如果你们测试的问卷有“上一页”“下一页”按钮来回跳转MutationObserver 的这种写法也能重新覆盖到新显示的题目算是热词水友一直在找的一个优化点。4. 常见问题与排查技巧实录4.1 脚本不生效的排查这个问题几乎占了所有反馈的一半。最常见的几个原因match 规则没有覆盖当前域名。问卷星有wjx.cn、wjx.top还有各种自定义域名比如ks.wjx.top、www.1diaocha.com这些都得在match里加上。一个通用做法是直接放宽到*://*/*survey*之类的模糊匹配但我不推荐容易误伤其他网站。页面加载事件没触发。如果你打开的是iframe嵌套的问卷window.load事件可能不会正常触发或者触发时机不对。这时保险的做法是把主函数绑定在 DOMContentLoaded同时加一个兜底定时器。控制台有红字报错。最典型的是某个选择器挂掉了导致脚本中断检查错误堆栈里querySelector返回null的地方改用可选链操作符或者加判断就可以。4.2 选项能点到但提交校验不通过这种情况一般是联动题或逻辑题的坑。问卷星的题目可以设置“选择A则跳转到某题”当脚本随机到了带跳转的选项后面的题被隐藏此时用validateAllAnswered校验时会发现某些必答题不见了——不是没填而是根本不需要填。解决办法是校验函数要顺带判断元素是否在页面中可见如果元素被隐藏直接跳过。function isVisible(el) { const style window.getComputedStyle(el); return style.display ! none style.visibility ! hidden el.offsetParent ! null; }在 findQuestions 阶段就把不可见的题过滤掉校验时也只检查可见题目这样就不会误报漏填了。4.3 关于反作弊与数据安全的一点提醒写这个脚本的初衷是给自己做问卷测试和造数用的我有必要把这个边界说清楚。用它去刷别人的正常调研问卷不仅不道德而且容易被问卷系统通过答题时长、选项分布、IP频率等特征识别出来到时候账号异常或者被拉黑名单得不偿失。教育场景里用来模拟答题、做压力测试、数据清洗前的功能验证这些才是它发挥价值的地方。脚本本身不收集任何个人信息不访问你除了问卷星以外的任何页面所以从隐私角度看是干净的。但如果你改脚本时添加了自定义上传或统计功能请确保你完全清楚自己在做什么并且只把数据发给可信的服务器。4.4 踩过的其他小坑再分享三个我实际踩过的坑。第一个是 click 事件失效。问卷星部分选项是自定义渲染的点击label不一定能触发原生的click事件需要改成mousedown加mouseup的组合或者直接调input.click()。我写脚本时用的是混合策略优先点击input拿不到input再点击label。第二个是异步渲染导致找不到节点。问卷星的题目有时是接口返回后再渲染的页面 load 事件触发时HTML里还没有题目。除了 setTimeout 增加等待时间更稳的方案是轮询检查每隔500ms查一次题目数量直到数量稳定为止。我脚本里用的是setTimeout加一次重试如果发现题目为0会再跑一次最多重试5次。第三个是“今日已填写”之类的弹窗。如果IP或者手机号已经被记录过问卷星会弹窗提示导致后续点不了任何按钮。脚本目前不处理这种弹窗因为这是产品层面的拦截绕过去没有意义。如果你测试遇到这种换一个干净环境或者换一套测试数据就好。最后再分享一个使用技巧用这个脚本跑问卷测试我个人的习惯是跑完一轮后把validateAllAnswered的结果打印出来配合浏览器控制台观察选项分布。如果连续几轮都随机到同一批题目的相同选项说明随机种子或者洗牌逻辑偏了需要调一下多选题的选择数量范围和单选题的洗牌权重。另外建议给脚本加一个全局开关变量类似CONFIG.enabled true/false挂在油猴菜单里这样手动填写某份重要问卷时就不用禁用整个脚本直接在菜单里切换开关就行。这个小功能能省掉不少来回编辑脚本的时间实测下来很值得加。本文还有配套的精品资源点击获取