JavaScript——易班优课YOOC课群在线测试自动答题解决方案(二)答案解析与提交

张开发
2026/4/21 0:18:50 15 分钟阅读

分享文章

JavaScript——易班优课YOOC课群在线测试自动答题解决方案(二)答案解析与提交
1. 答案数据解析实战在上一篇文章中我们已经成功获取了YOOC平台的题目DOM数据。现在需要把这些原始HTML转换成可操作的答案结构。我实测发现YOOC的题目DOM有个特点——正确答案其实就藏在页面里只是被CSS隐藏了。先来看个典型的多选题DOM结构div classquestion-board idquestion-123 div classquestion-stem下列哪些是JavaScript的基本数据类型/div ul classoptions li classoption correct>function parseAnswers(html) { const parser new DOMParser(); const doc parser.parseFromString(html, text/html); const questions Array.from(doc.getElementsByClassName(question-board)); return questions.map(q { const correctOptions Array.from(q.querySelectorAll(.option.correct)) .map(opt opt.dataset.optionId); return { id: q.id.replace(question-, ), correctAnswers: correctOptions }; }); }这个方案我测试过三个不同版本的YOOC页面都能稳定识别答案。有个坑要注意有些题目会用hidden属性而非CSS类来隐藏答案这时候需要调整选择器为[data-correcttrue]。2. 自动填答技术实现拿到解析后的答案数组后下一步是模拟用户操作。经过反复测试我发现直接设置checked属性是无效的——YOOC的前端框架会监听change事件。这里分享两种实测有效的方案2.1 事件触发法function fillAnswers(answers) { answers.forEach(ans { const questionId question-${ans.id}; ans.correctAnswers.forEach(optionId { const checkbox document.querySelector( #${questionId} input[data-option-id${optionId}] ); if (checkbox) { checkbox.checked true; const event new Event(change, { bubbles: true }); checkbox.dispatchEvent(event); } }); }); }2.2 表单操作法更稳定的方案是直接操作YOOC使用的FormDatafunction buildFormData(answers) { const form document.querySelector(form.exam-form); const formData new FormData(form); answers.forEach(ans { ans.correctAnswers.forEach(optionId { formData.append(answers[${ans.id}], optionId); }); }); return formData; }我建议优先使用第二种方案它在YOOC最近的改版中表现更稳定。实测时发现多选题需要特别注意——同一个问题ID要多次append值。3. 自动提交的完整流程完整的自动提交需要处理三个关键环节防检测机制添加随机延迟建议2-5秒模拟人工操作function randomDelay() { return new Promise(resolve setTimeout(resolve, 2000 Math.random() * 3000) ); }提交验证处理YOOC的CSRF保护async function submitExam(formData) { await randomDelay(); const csrfToken document.querySelector(meta[namecsrf-token]).content; const headers { X-CSRF-Token: csrfToken, Accept: application/json }; const response await fetch(/exams/submit, { method: POST, headers, body: formData }); return response.json(); }结果验证检查提交是否成功async function autoSubmit(answers) { try { const formData buildFormData(answers); const result await submitExam(formData); if (result.success) { console.log(提交成功得分, result.score); } else { console.error(提交失败, result.message); } } catch (error) { console.error(发生异常, error); } }我在实际使用中发现最好在提交前先调用一次/exams/validate接口预校验答案可以避免因网络问题导致的重复提交。4. 异常处理与调试技巧做自动化脚本最头疼的就是平台更新导致的失效。经过多次踩坑我总结出这些调试经验DOM变化监测定期检查元素选择器是否有效// 在控制台测试关键选择器 document.querySelectorAll(.question-board).length 0请求监控使用浏览器开发者工具的Network面板过滤XHR请求检查提交请求的载荷格式关注响应状态码错误恢复实现自动重试机制async function safeSubmit(formData, retries 3) { while (retries--) { try { return await submitExam(formData); } catch (error) { if (retries 0) throw error; await new Promise(resolve setTimeout(resolve, 5000)); } } }特别提醒YOOC有时会更新CSRF令牌的获取方式。如果突然提交失败首先检查meta[namecsrf-token]是否存在或者改为从window全局对象获取const csrfToken window.YOOC_Config?.csrfToken;5. 浏览器扩展集成方案为了方便日常使用我最终把这个脚本做成了Chrome扩展。核心结构如下// background.js chrome.runtime.onMessage.addListener((request, sender, sendResponse) { if (request.action fetchAnswers) { fetchAnswers().then(sendResponse); return true; } if (request.action autoSubmit) { autoSubmit(request.answers).then(sendResponse); return true; } }); // content.js function injectScript() { const script document.createElement(script); script.src chrome.runtime.getURL(inject.js); document.head.appendChild(script); } chrome.runtime.onMessage.addListener((message) { if (message.type FILL_ANSWERS) { fillAnswers(message.answers); } });扩展打包时要注意在manifest.json声明所需权限{ permissions: [activeTab, storage], host_permissions: [*://*.yooc.me/*] }处理跨域限制// 在background.js中使用fetch时 const response await fetch(url, { credentials: include });这个方案我已经稳定使用了一年多配合定时任务可以批量处理多个课群的测试。不过要注意合理使用建议仅用于复习时的自测场景。

更多文章