news 2026/9/8 10:02:27

问卷星自动随机填写脚本:油猴插件实现一键批量答题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
问卷星自动随机填写脚本:油猴插件实现一键批量答题

简介:这是一款面向问卷填写场景的浏览器端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.cnwww.wjx.top两个域名,因为问卷星的正式环境和测试环境经常换着用,少写一个脚本就静默失效。每次操作之间用随机延迟而不是立刻执行,是为了模拟真人答题的节奏,避免短时间大量请求触发风控。

2.2 单选题、多选题与填空题的随机策略

接下来是重头戏,把题目处理函数补全。问卷星一个题目的典型结构是这样的:题干在div.field-title里,选项是div.ui-control-group下的label.ui-checkboxlabel.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[type="text"]') || 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[type="radio"]'); const checkboxes = questionDiv.querySelectorAll('input[type="checkbox"]'); const textareas = questionDiv.querySelectorAll('textarea'); const textInputs = questionDiv.querySelectorAll('input[type="text"], 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[type="radio"]'); 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[type="radio"]:checked') !== null; const checkboxChecked = div.querySelectorAll('input[type="checkbox"]:checked').length > 0; const textFilled = [...div.querySelectorAll('textarea')].every(t => t.value.trim() !== ''); const textInputFilled = [...div.querySelectorAll('input[type="text"]')].every(t => t.value.trim() !== ''); if (!radioChecked && !checkboxChecked && !textFilled && !textInputFilled) { unanswered.push(idx + 1); } }); return unanswered; }

这个函数返回所有未填写的必答题编号,我在提交前调用,有遗漏就先补上,而不是傻乎乎直接点提交。这个方法在问卷跳题逻辑多的时候尤其有用。

3. 实操配置:从安装到平滑运行

3.1 油猴扩展的安装与脚本写入

使用这个脚本前,你需要先给浏览器装一个用户脚本管理器。Chrome/Edge 推荐 Tampermonkey,Firefox 就装 Greasemonkey 或者 Violentmonkey,都行。装好扩展后,点浏览器右上角的扩展图标,选择“添加新脚本”,把第2小节的完整代码粘进去,Ctrl+S 保存。

保存完建议先不要直接去问卷页测试,先看脚本管理器的页面右上角有没有报错图标。很多人卡在这一步:脚本没生效但不知道去哪看错误。油猴的好处是它有独立的控制台入口,点击弹窗里的“调试”或者按 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.cnwjx.top,还有各种自定义域名,比如ks.wjx.topwww.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事件,需要改成mousedownmouseup的组合,或者直接调input.click()。我写脚本时用的是混合策略,优先点击input,拿不到input再点击label

第二个是异步渲染导致找不到节点。问卷星的题目有时是接口返回后再渲染的,页面 load 事件触发时HTML里还没有题目。除了 setTimeout 增加等待时间,更稳的方案是轮询检查:每隔500ms查一次题目数量,直到数量稳定为止。我脚本里用的是setTimeout加一次重试,如果发现题目为0会再跑一次,最多重试5次。

第三个是“今日已填写”之类的弹窗。如果IP或者手机号已经被记录过,问卷星会弹窗提示,导致后续点不了任何按钮。脚本目前不处理这种弹窗,因为这是产品层面的拦截,绕过去没有意义。如果你测试遇到这种,换一个干净环境或者换一套测试数据就好。

最后再分享一个使用技巧

用这个脚本跑问卷测试,我个人的习惯是跑完一轮后把validateAllAnswered的结果打印出来,配合浏览器控制台观察选项分布。如果连续几轮都随机到同一批题目的相同选项,说明随机种子或者洗牌逻辑偏了,需要调一下多选题的选择数量范围和单选题的洗牌权重。

另外建议给脚本加一个全局开关变量,类似CONFIG.enabled = true/false,挂在油猴菜单里,这样手动填写某份重要问卷时就不用禁用整个脚本,直接在菜单里切换开关就行。这个小功能能省掉不少来回编辑脚本的时间,实测下来很值得加。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 10:02:19

Hermes Agent:具备自我进化能力的AI代理架构解析与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 9:59:22

企业级AI落地卡在最后一公里?FDE成为破局关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 9:59:19

Python+akshare获取黄金价格数据并可视化分析实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 9:58:39

基于微信小程序的宝宝成长记录系统(源码+lw+部署文档+讲解等)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/8 9:57:52

OMNeT++ 4.3源码包在Windows上的编译配置与排错实战

简介&#xff1a;Omnet是一套面向复杂网络系统建模的离散事件仿真框架&#xff0c;特别适合无线传感器网络和自组织网络的研究与开发。该资源提供Omnet 4.3在Windows平台上的完整源代码压缩包&#xff0c;大小约321.65MB。官方在4.3版本中引入了多项能力升级&#xff0c;包括NE…

作者头像 李华
网站建设 2026/9/8 9:57:52

基于SpringBoot的移动数字图书馆系统设计与实现

1. 项目整体设计与思路拆解1.1 移动数字图书馆到底要解决什么问题先说个我每年带毕设都会被问到的问题&#xff0c;也是这个项目的核心立足点——"数字图书馆都已经有那么多开源系统了&#xff0c;为什么还要自己做一个&#xff1f;"如果你只是做一个PC端的图书管理系…

作者头像 李华