您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动填写问卷星表单中的个人信息 - 页面加载时自动启动
// ==UserScript== // @name 问卷星自动填写助手 - 自启动版 // @namespace http://tampermonkey.net/ // @version 3.0 // @description 自动填写问卷星表单中的个人信息 - 页面加载时自动启动 // @author Jiashi // @match *://*.wjx.cn/* // @match *://*.wenjuan.com/* // @match *://*.wenjuanxing.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // 用户信息配置 - 请根据实际情况修改 const userInfo = { name: "张三", studentId: "20230001", gender: "男", college: "计算机学院", grade: "2023级", major: "计算机科学与技术", phone: "13800138000", email: "[email protected]", age: "20", class: "计科2301班" }; // 优化的字段映射,更精确的匹配规则 const fieldMappings = { college: ['学院', 'college', 'school', '所在学院', '学院名称', '请选择学院', 'department', '院系', '所属学院'], studentId: ['学号', 'student id', 'studentid', '学生学号', '请输入学号', '你的学号', 'student number', '学生编号', '学籍号'], name: ['姓名', 'name', '真实姓名', '你的姓名', '请输入姓名', '请填写姓名', 'fullname', 'real name', '学生姓名', '本人姓名'], gender: ['性别', 'gender', 'sex', '请选择性别', '你的性别', '男女'], grade: ['年级', 'grade', '所在年级', '请选择年级', '入学年份', '届', '级别'], major: ['专业', 'major', '所学专业', '专业名称', '请选择专业', '学科专业', '所学学科'], phone: ['手机', 'phone', '电话', '手机号', '联系电话', '手机号码', 'mobile', '电话号码', '联系方式'], email: ['邮箱', 'email', '电子邮箱', '邮件地址', 'e-mail', '电子邮件', '邮件'], age: ['年龄', 'age', '请输入年龄', '你的年龄', '岁数'], class: ['班级', 'class', '所在班级', '班级名称', '请选择班级', '班'] }; // 已填写的输入框记录,避免重复填写 const filledInputs = new Set(); function showMessage(message, type = 'info') { const colors = { info: '#007bff', success: '#28a745', warning: '#ffc107', error: '#dc3545' }; const messageDiv = document.createElement('div'); messageDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${colors[type]}; color: white; padding: 12px 20px; border-radius: 6px; font-size: 14px; z-index: 999999; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-width: 300px; `; messageDiv.textContent = message; document.body.appendChild(messageDiv); setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 4000); } // 改进的精确匹配函数 function preciseMatch(text, keywords) { if (!text) return 0; const lowerText = text.toLowerCase().trim().replace(/[\s\*\::\.\。]/g, ''); let maxScore = 0; for (const keyword of keywords) { const lowerKeyword = keyword.toLowerCase().replace(/[\s]/g, ''); let score = 0; // 精确匹配得分最高 if (lowerText === lowerKeyword) { score = 100; } // 完全包含匹配 else if (lowerText.includes(lowerKeyword)) { score = 80; } else if (lowerKeyword.includes(lowerText) && lowerText.length >= 2) { score = 70; } // 部分匹配 else { const commonLength = getCommonLength(lowerText, lowerKeyword); if (commonLength >= 2) { score = (commonLength / Math.max(lowerText.length, lowerKeyword.length)) * 60; } } maxScore = Math.max(maxScore, score); } return maxScore; } function getCommonLength(str1, str2) { let maxLength = 0; for (let i = 0; i < str1.length; i++) { for (let j = 0; j < str2.length; j++) { let length = 0; while (i + length < str1.length && j + length < str2.length && str1[i + length] === str2[j + length]) { length++; } maxLength = Math.max(maxLength, length); } } return maxLength; } // 改进的标签查找函数 function findLabelForInput(input) { try { let labelTexts = []; // 方法1: 通过 for 属性查找 if (input.id) { const label = document.querySelector(`label[for="${input.id}"]`); if (label) labelTexts.push(label.textContent.trim()); } // 方法2: 查找最近的父级容器中的标签 let parent = input.parentElement; for (let i = 0; i < 4 && parent; i++) { // 优先查找直接的label标签 const directLabels = parent.querySelectorAll(':scope > label'); for (const label of directLabels) { const text = label.textContent.trim(); if (text && text.length < 30) { labelTexts.push(text); } } // 查找其他文本元素,但排除过长的文本 const textElements = parent.querySelectorAll('span, div'); for (const el of textElements) { const text = el.textContent.trim(); if (text && text.length > 1 && text.length < 20 && !text.includes('请选择') && !text.includes('请输入') && !text.includes('必填') && !text.includes('*')) { // 检查是否是直接包含input的容器的文本 if (el.contains(input) || input.contains(el)) continue; labelTexts.push(text); } } parent = parent.parentElement; } // 方法3: 查找前面的兄弟元素 let sibling = input.previousElementSibling; let siblingCount = 0; while (sibling && siblingCount < 3) { if (['LABEL', 'SPAN', 'DIV'].includes(sibling.tagName)) { const text = sibling.textContent.trim(); if (text && text.length > 1 && text.length < 20) { labelTexts.push(text); } } sibling = sibling.previousElementSibling; siblingCount++; } // 方法4: 使用 placeholder 和 title if (input.placeholder && input.placeholder.length < 20) { labelTexts.push(input.placeholder); } if (input.title && input.title.length < 20) { labelTexts.push(input.title); } // 清理和去重,优先保留较短的文本 labelTexts = [...new Set(labelTexts)] .filter(text => text && text.length > 0) .sort((a, b) => a.length - b.length) .slice(0, 3); // 只保留前3个最相关的标签 return labelTexts.join(' | '); } catch (error) { console.log('查找标签时出错:', error); return ''; } } // 获取输入框的唯一标识 function getInputIdentifier(input) { const parts = []; if (input.id) parts.push('id:' + input.id); if (input.name) parts.push('name:' + input.name); if (input.className) parts.push('class:' + input.className); // 添加DOM路径作为后备标识 let element = input; let path = []; while (element && element !== document.body && path.length < 5) { let selector = element.tagName.toLowerCase(); if (element.id) { selector += '#' + element.id; } path.unshift(selector); element = element.parentElement; } parts.push('path:' + path.join('>')); return parts.join('|'); } // 智能字段匹配和填写 function smartFillForm() { let filledCount = 0; let totalAttempts = 0; showMessage('正在智能识别并填写表单...', 'info'); console.log('=== 开始智能填写表单 ==='); console.log('用户信息:', userInfo); // 查找所有可能的输入元素 const selectors = [ 'input[type="text"]', 'input[type="email"]', 'input[type="tel"]', 'input[type="number"]', 'input[type="radio"]', 'input[type="checkbox"]', 'select', 'textarea', 'input:not([type])', 'input[type=""]' ]; const allInputs = document.querySelectorAll(selectors.join(',')); console.log(`页面总共找到 ${allInputs.length} 个输入元素`); // 为每个输入框找到最佳匹配的字段 const inputMatches = []; for (const input of allInputs) { // 跳过隐藏元素和已填写的元素 if (input.style.display === 'none' || input.hidden || input.offsetParent === null || input.value.trim() !== '') { continue; } const identifier = getInputIdentifier(input); if (filledInputs.has(identifier)) { continue; } const labelText = findLabelForInput(input); if (!labelText) continue; console.log(`\n检查输入框: ${input.tagName}[${input.type || 'default'}] - 标签: "${labelText}"`); // 计算与每个字段的匹配分数 let bestMatch = null; let bestScore = 0; for (const [fieldType, keywords] of Object.entries(fieldMappings)) { const score = preciseMatch(labelText, keywords); console.log(` ${fieldType}: ${score}分`); if (score > bestScore && score >= 60) { // 设置最低匹配分数阈值 bestMatch = { fieldType, score, value: userInfo[fieldType] }; bestScore = score; } } if (bestMatch && bestMatch.value) { inputMatches.push({ input, identifier, labelText, fieldType: bestMatch.fieldType, value: bestMatch.value, score: bestMatch.score, type: input.type || input.tagName.toLowerCase() }); console.log(`✓ 最佳匹配: ${bestMatch.fieldType} (${bestMatch.score}分) = ${bestMatch.value}`); } } // 按匹配分数排序,优先填写匹配度最高的 inputMatches.sort((a, b) => b.score - a.score); console.log(`\n=== 准备填写 ${inputMatches.length} 个字段 ===`); // 分字段类型填写,避免同一字段重复填写 const fieldUsed = new Set(); for (const match of inputMatches) { // 如果该字段类型已经填写过,跳过 if (fieldUsed.has(match.fieldType)) { console.log(`跳过重复字段: ${match.fieldType}`); continue; } totalAttempts++; console.log(`\n填写字段: ${match.fieldType} = ${match.value} (${match.score}分)`); console.log(`目标输入框: ${match.type} - "${match.labelText}"`); let success = false; try { if (match.type === 'radio') { success = setRadioValue(match.input, match.value); } else if (match.type === 'select' || match.input.tagName === 'SELECT') { success = setSelectValue(match.input, match.value); } else { success = setInputValue(match.input, match.value); } if (success) { filledCount++; fieldUsed.add(match.fieldType); filledInputs.add(match.identifier); console.log(`✓ 成功填写: ${match.fieldType} = ${match.value}`); } else { console.log(`✗ 填写失败: ${match.fieldType} = ${match.value}`); } } catch (error) { console.log(`填写字段 ${match.fieldType} 时出错:`, error); } } // 显示结果 setTimeout(() => { if (filledCount > 0) { showMessage(`智能填写完成!成功填写 ${filledCount} 个字段`, 'success'); console.log(`\n=== 填写成功的字段 ===`); for (const fieldType of fieldUsed) { console.log(`✓ ${fieldType}: ${userInfo[fieldType]}`); } } else { showMessage('未找到可填写的字段', 'warning'); // 调试信息 console.log('\n=== 调试信息:页面中的所有输入元素 ==='); const debugInputs = document.querySelectorAll('input, select, textarea'); debugInputs.forEach((el, index) => { const label = findLabelForInput(el); const isVisible = el.offsetParent !== null && el.style.display !== 'none'; const hasValue = el.value && el.value.trim() !== ''; console.log(`${index + 1}. ${el.tagName}[${el.type || 'default'}] - 标签: "${label}" - 可见: ${isVisible} - 有值: ${hasValue}`); }); } console.log(`\n=== 智能填写完成: ${filledCount}/${totalAttempts} 个字段 ===`); }, 500); } // 设置输入框值的函数 function setInputValue(input, value) { try { if (!input || !value) return false; console.log(`设置文本值: ${value}`); // 聚焦并清空 input.focus(); input.value = ''; // 设置新值 input.value = value; // 触发事件 const events = ['input', 'change', 'blur']; for (const eventType of events) { const event = new Event(eventType, { bubbles: true, cancelable: true }); input.dispatchEvent(event); } // 确认值已设置 return input.value === value; } catch (error) { console.log('设置输入值时出错:', error); return false; } } function setRadioValue(input, value) { try { // 查找同组的所有radio按钮 const radioGroup = input.name ? document.querySelectorAll(`input[name="${input.name}"]`) : [input]; for (const radio of radioGroup) { const label = findLabelForInput(radio); if (label.includes(value) || radio.value === value || value.includes(label) || value.includes(radio.value)) { radio.checked = true; radio.dispatchEvent(new Event('change', { bubbles: true })); return true; } } return false; } catch (error) { console.log('设置单选按钮时出错:', error); return false; } } function setSelectValue(select, value) { try { for (const option of select.options) { if (option.text.includes(value) || option.value.includes(value) || value.includes(option.text) || value.includes(option.value)) { select.value = option.value; select.dispatchEvent(new Event('change', { bubbles: true })); return true; } } return false; } catch (error) { console.log('设置下拉选择时出错:', error); return false; } } // 智能等待页面加载完成 function waitForPageReady() { return new Promise((resolve) => { // 检查页面是否包含表单元素 function checkForForms() { const inputs = document.querySelectorAll('input, select, textarea'); const hasVisibleInputs = Array.from(inputs).some(input => input.offsetParent !== null && input.style.display !== 'none' ); if (hasVisibleInputs) { console.log('检测到表单元素,准备开始填写'); resolve(); } else { console.log('等待表单元素加载...'); setTimeout(checkForForms, 500); } } checkForForms(); }); } // 主初始化函数 async function initialize() { console.log('问卷星自动填写助手已加载 - 自启动模式'); // 等待页面准备就绪 await waitForPageReady(); // 等待额外的时间确保页面完全渲染 setTimeout(() => { smartFillForm(); }, 1500); // 监听页面变化,适应动态加载的内容 const observer = new MutationObserver((mutations) => { let shouldRecheck = false; for (const mutation of mutations) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { // 检查是否有新的表单元素添加 for (const node of mutation.addedNodes) { if (node.nodeType === 1) { // Element node const hasInputs = node.tagName === 'INPUT' || node.tagName === 'SELECT' || node.tagName === 'TEXTAREA' || node.querySelectorAll('input, select, textarea').length > 0; if (hasInputs) { shouldRecheck = true; break; } } } } if (shouldRecheck) break; } if (shouldRecheck) { console.log('检测到新的表单元素,重新执行填写'); setTimeout(smartFillForm, 1000); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 页面加载完成后自动启动 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { setTimeout(initialize, 1000); } // 确保在页面完全加载后也能工作 window.addEventListener('load', () => { setTimeout(initialize, 2000); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址