// ==UserScript== // @name 超星自测作业题目采集器 // @namespace 题目采集 // @version 1.0.2 // @description 支持一键解析课程题目,智能识别单选/多选/填空/判断等题型,可导出为CSV/Excel格式。提供可视化预览 -- 对新版自测及作业适配 // @author Jason7187 // @match *://mooc1.chaoxing.com/mooc-ans/mooc2/work/* // @match *://mooc1.chaoxing.com/exam-ans/exam/* // @icon https://maxpcimg.online/i/2025/04/11/67f8656abe8db.png // @grant GM_registerMenuCommand // @grant GM_notification // @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js // ==/UserScript== (function() { 'use strict'; // ================= 全局配置 ================= const CONFIG = { DELAY_INIT: 2000, ANSWER_SPLITTER: '###', OPTION_SPLITTER: ' | ', PREVIEW_LIMIT: 100, HOTKEYS: { SHOW: 'ArrowRight', HIDE: 'ArrowLeft' } }; let currentData = []; let isToolbarVisible = true; // ================= 核心解析模块 ================= class CXParser { static parseAll() { const { courseName, courseId } = this.getCourseInfo(); return Array.from(document.querySelectorAll('.questionLi')).map(container => { const type = this.parseType(container); return { courseName, courseId, type, question: this.parseQuestion(container), options: this.parseOptions(container), answer: this.parseAnswer(container, type) }; }).filter(item => item.answer); } static getCourseInfo() { return { courseName: document.querySelector('h2.mark_title')?.textContent.trim() || '未知课程', courseId: new URLSearchParams(location.search).get('courseId') || '未知ID' }; } static parseType(container) { return (container.querySelector('.colorShallow')?.textContent.trim() || '') .replace(/[()()]/g, ''); } static parseQuestion(container) { return (container.querySelector('.qtContent')?.textContent || '') .replace(/\s+/g, ' ') .trim(); } static parseOptions(container) { return Array.from(container.querySelectorAll('.mark_letter li')) .map(li => li.textContent.trim()) .join(CONFIG.OPTION_SPLITTER); } static parseAnswer(container, type) { try { const answerElement = container.querySelector('.rightAnswerContent, .colorGreen'); let rawAnswer = answerElement?.textContent || ''; // 统一清理答案前缀 rawAnswer = rawAnswer.replace(/^[\s\S]*?[::]\s*/, '').trim(); if (['单选题', '多选题'].includes(type)) { const optionsMap = this.buildOptionsMap(container); return rawAnswer.split('') .map(c => this.extractOptionText(optionsMap[c])) .filter(Boolean) .join(type === '多选题' ? CONFIG.ANSWER_SPLITTER : ''); } if (type === '填空题') { return rawAnswer.split(/(?:$|()\d+(?:$|))/g) .map(s => s.trim().replace(/^[::]\s*/, '')) .filter(Boolean) .join(CONFIG.ANSWER_SPLITTER); } return this.formatJudgmentAnswer(rawAnswer); } catch (e) { console.error('解析失败:', e); return ''; } } static buildOptionsMap(container) { return Array.from(container.querySelectorAll('.mark_letter li')).reduce((map, li, index) => { const key = String.fromCharCode(65 + index); map[key] = li.textContent.trim(); return map; }, {}); } static extractOptionText(fullOption) { return fullOption?.replace(/^([A-Z])[..。]?\s*/, '') || ''; } static formatJudgmentAnswer(text) { return text.replace(/√/, '正确').replace(/×/, '错误'); } } // ================= 数据导出模块 ================= class DataExporter { static exportCSV(data) { const escapeCSV = (text) => { if (/[\n\t"]/.test(text)) { return `"${text.replace(/"/g, '""')}"`; } return text; }; const content = data.map(item => [ item.courseName, item.courseId, item.type, item.question, item.options, item.answer ].map(escapeCSV).join('\t')).join('\n'); this.downloadFile("\uFEFF" + content, `${this.getFileName()}.csv`, 'text/csv;charset=utf-8;' ); } static exportExcel(data) { const worksheet = XLSX.utils.json_to_sheet(data.map(item => ({ '课程名称': item.courseName, '课程ID': item.courseId, '题型': item.type, '题目内容': item.question, '选项': item.options, '正确答案': item.answer }))); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, '题目数据'); XLSX.writeFile(workbook, `${this.getFileName()}.xlsx`); } static downloadFile(content, fileName, type) { const blob = new Blob([content], { type }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = fileName; link.click(); } static getFileName() { const date = new Date().toISOString().slice(0, 10).replace(/-/g, ''); return `超星题目_${CXParser.getCourseInfo().courseName}_${date}`; } } // ================= 预览界面模块 ================= class PreviewUI { static show(data) { this.close(); const preview = this.createPreview(data); this.injectStyles(); document.body.appendChild(preview); } static createPreview(data) { const preview = document.createElement('div'); preview.id = 'cx-preview'; preview.innerHTML = `
课程名称 | 课程ID | 题型 | 题目内容 | 选项 | 正确答案 |
---|---|---|---|---|---|
${item.courseName} | ${item.courseId} | ${item.type} | ${item.question} | ${item.options.replace(/\|/g, ' ')} |
${item.answer} |