Greasy Fork

自动解析验证码脚本

自动解析网站验证码并填充

目前为 2025-04-16 提交的版本。查看 最新版本

// ==UserScript==
// @name         自动解析验证码脚本
// @namespace    http://tampermonkey.net/
// @version      1.0.2
// @description  自动解析网站验证码并填充
// @author       shen chen
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
const backUrl = "http://47.98.114.76:81/verification-code";

// 定义一个包含目标网站信息的列表
const siteList = [
    {
        url: "https://zkpt.zj.msa.gov.cn/#/login", // 目标网站URL
        imageXpath: "//img[@alt='验证码']", // 验证码图片的XPath
        valueXpath: "//input[@placeholder='图片验证码']" // 验证码输入框的XPath
    },
    {
        url: "http://10.30.10.42:9080/user/login?redirect=%2F",
        imageXpath: "//div[@class='ant-col ant-col-8']//img[1]",
        valueXpath: "(//input[@class='ant-input ant-input-lg'])[3]"
    },
    {
        url: "http://121.41.173.27:9080/user/login",
        imageXpath: "//div[@class='ant-col ant-col-8']//img[1]",
        valueXpath: "(//input[@class='ant-input ant-input-lg'])[3]"
    },
    {
        url: "https://hhly.chinabeston.com:4443/login/#/login/iev",
        imageXpath: "//div[@class='el-col el-col-8']//img[1]",
        valueXpath: "//input[@placeholder='请输入验证码']"
    },
    // 可以添加更多网站配置

];

/**
 * 根据XPath获取DOM元素
 * @param {string} xpath - XPath表达式
 * @returns {HTMLElement|null} - 匹配到的元素或null
 */
function getElementByXpath(xpath) {
    const result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    return result.singleNodeValue;
}


/**
 * 将图片元素转换为Base64编码
 * @param {HTMLImageElement} imageElement - 图片元素
 * @param {function} callback - 回调函数,返回Base64字符串
 */
function getCaptchaImageAsBase64(img, callback) {
    img.crossOrigin = 'Anonymous'; // 处理跨域问题

    img.onload = function () {
        const canvas = document.createElement('canvas');
        canvas.width = 120;
        canvas.height = 40;
        // console.log('图片尺寸:', img.width, img.height)
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0);

        // 获取Base64编码
        const dataURL = canvas.toDataURL('image/png');
        // console.log('Base64编码:',dataURL);
        callback(dataURL);
    };
    img.onerror = function () {
        console.error('图片加载失败');
        callback(null);
    };

}

/**
 * 发送验证码图片到后端接口并填充解析结果
 * @param {string} base64Image - 验证码图片的URL
 * @param {string} valueXpath - 验证码输入框的XPath
 */
function sendCaptchaToBackend(base64Image, valueXpath) {
    // console.log("发送验证码图片到后端接口:");
    GM_xmlhttpRequest({
        method: "POST",
        url: backUrl,
        headers: {
            "Content-Type": "application/json"
        },
        data: JSON.stringify({image_base64: base64Image}),
        onload: function (response) {
            const data = JSON.parse(response.responseText);
            if (data.captcha_value) {
                // console.log("验证码解析成功:", data.captcha_value);

                // 自动填写验证码到输入框
                const captchaInput = getElementByXpath(valueXpath);
                if (captchaInput) {
                    captchaInput.value = data.captcha_value;
                    // console.log("验证码已自动填写");
                } else {
                    console.error("未找到验证码输入框");
                }
            } else {
                console.error("验证码解析失败:", data.error || "未知错误");
            }
        },
        onerror: function (error) {
            console.error("请求后端失败:", error);
        }
    });

}

/**
 * 根据XPath获取异步加载的DOM元素
 * @param {string} xpath - XPath表达式
 * @param {number} timeout - 超时时间(毫秒),默认5000ms
 * @returns {Promise<HTMLElement|null>} - 匹配到的元素或null
 */
function waitForElementByXpath(xpath, timeout = 5000) {
    return new Promise((resolve, reject) => {
        const startTime = Date.now();

        // 使用 MutationObserver 监听 DOM 变化
        const observer = new MutationObserver(() => {
            const element = getElementByXpath(xpath);
            if (element) {
                observer.disconnect(); // 停止观察
                resolve(element);
            } else if (Date.now() - startTime > timeout) {
                observer.disconnect(); // 超时后停止观察
                reject(new Error("超时未找到元素"));
            }
        });

        // 开始观察整个文档的变化
        observer.observe(document.body, {childList: true, subtree: true});

        // 如果超时仍未找到元素,拒绝 Promise
        setTimeout(() => {
            observer.disconnect();
            reject(new Error("超时未找到元素"));
        }, timeout);
    });
}





(function () {
    'use strict';

    // 检查当前网站是否在列表中
    const currentUrl = window.location.href;
    const siteConfig = siteList.find(site => currentUrl.includes(site.url));

    if (!siteConfig) {
        console.log("当前网站不在目标列表中");
        return;
    }

    console.log("匹配到目标网站:", siteConfig.url);
    // 获取验证码图片元素
    waitForElementByXpath(siteConfig.imageXpath)
        .then((element) => {
            // console.log("找到元素:", element);
            if (!element) {
                console.error("未找到验证码图片元素");
                return;
            }
            // 获取图片的二进制数据(Base64编码)
            getCaptchaImageAsBase64(element, (base64Image) => {
                if (base64Image) {
                    // console.log("成功获取验证码图片的Base64编码");
                    // 发送验证码图片到后端接口进行解析
                    sendCaptchaToBackend(base64Image, siteConfig.valueXpath);
                } else {
                    console.error("无法获取验证码图片的Base64编码");
                }
            });
        }).catch((error) => {
        console.error(error.message);
    });

})();