Google Search Result Extractor

Automatically extract Google search results (Title, URL) and navigate to the next page.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Google Search Result Extractor
// @name:zh-CN   谷歌搜索结果自动提取器
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Automatically extract Google search results (Title, URL) and navigate to the next page.
// @description:zh-CN 自动提取谷歌搜索结果的标题和链接,并消耗自动翻页提取至最后一页。
// @author       YourName
// @match        https://www.google.com/search*
// @match        https://www.google.com.hk/search*
// @match        https://www.google.ad/search*
// @match        https://www.google.ae/search*
// @match        https://www.google.com.tw/search*
// @include      /^https:\/\/www\.google\..*\/search.*/
// @grant        GM_setClipboard
// @grant        GM_notification
// @license      MIT
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // 1. UI 面板构建
    const createUI = () => {
        const panel = document.createElement('div');
        panel.id = 'gs-extractor-panel';
        panel.style = `
            position: fixed; top: 20px; left: 20px; z-index: 10001;
            background: #ffffff; border: 1px solid #4285f4;
            padding: 15px; border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.15);
            width: 210px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        `;
        panel.innerHTML = `
            <div style="font-weight: bold; color: #4285f4; margin-bottom: 10px; font-size: 16px; border-bottom: 1px solid #eee; padding-bottom: 5px;">🔍 提取助手</div>
            <div style="margin-bottom: 12px; font-size: 13px;">已保存: <b id="gs-count" style="color: #d93025;">0</b> 条数据</div>
            <button id="gs-start" style="width: 100%; padding: 10px; background: #4285f4; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600; margin-bottom: 8px; transition: 0.3s;">🚀 开始自动提取</button>
            <button id="gs-copy" style="width: 100%; padding: 8px; background: #34a853; color: white; border: none; border-radius: 6px; cursor: pointer; margin-bottom: 8px;">📋 复制 (Excel格式)</button>
            <button id="gs-clear" style="width: 100%; padding: 6px; background: #fff; color: #666; border: 1px solid #ccc; border-radius: 6px; cursor: pointer; font-size: 12px;">🗑️ 清空缓存</button>
            <div id="gs-loader" style="display:none; color: #ea4335; font-size: 11px; text-align: center; margin-top: 8px; font-style: italic;">正在自动翻页中...</div>
        `;
        document.body.appendChild(panel);
    };

    // 2. 数据处理逻辑
    const getData = () => JSON.parse(localStorage.getItem('gs_ext_data') || '[]');
    const setData = (data) => localStorage.setItem('gs_ext_data', JSON.stringify(data));

    const updateUIStatus = () => {
        const count = getData().length;
        const countEl = document.getElementById('gs-count');
        if (countEl) countEl.innerText = count;
    };

    const extract = () => {
        let currentData = getData();
        const results = document.querySelectorAll('h3');
        let added = 0;

        results.forEach(h3 => {
            const a = h3.closest('a');
            if (a && a.href && !a.href.includes('google.com')) {
                const url = a.href;
                if (!currentData.some(item => item.url === url)) {
                    currentData.push({ title: h3.innerText.replace(/\s+/g, ' '), url: url });
                    added++;
                }
            }
        });

        setData(currentData);
        updateUIStatus();
        return added;
    };

    const runAuto = () => {
        document.getElementById('gs-loader').style.display = 'block';
        document.getElementById('gs-start').innerText = "⏹ 停止提取";
        localStorage.setItem('gs_is_running', 'true');

        extract();

        const nextBtn = document.querySelector('a#pnnext');
        if (nextBtn) {
            // 随机延迟 2.5s - 4.5s 保护账号
            const delay = Math.floor(Math.random() * 2000) + 2500;
            setTimeout(() => {
                if(localStorage.getItem('gs_is_running') === 'true') nextBtn.click();
            }, delay);
        } else {
            stopAuto();
            alert("✅ 提取完毕!已到达最后一页。");
        }
    };

    const stopAuto = () => {
        localStorage.setItem('gs_is_running', 'false');
        const startBtn = document.getElementById('gs-start');
        if (startBtn) {
            startBtn.innerText = "🚀 开始自动提取";
            document.getElementById('gs-loader').style.display = 'none';
        }
    };

    // 3. 事件挂载
    const init = () => {
        createUI();
        updateUIStatus();

        document.getElementById('gs-start').onclick = () => {
            if (localStorage.getItem('gs_is_running') === 'true') {
                stopAuto();
            } else {
                runAuto();
            }
        };

        document.getElementById('gs-copy').onclick = () => {
            const data = getData();
            if (data.length === 0) return alert("没有数据可供复制");
            const output = data.map(i => `${i.title}\t${i.url}`).join('\n');
            
            // 使用 GM_setClipboard 确保兼容性
            navigator.clipboard.writeText(output).then(() => {
                alert("已成功复制到剪贴板!");
            });
        };

        document.getElementById('gs-clear').onclick = () => {
            if (confirm("确定清除所有本地缓存的数据吗?")) {
                localStorage.removeItem('gs_ext_data');
                stopAuto();
                updateUIStatus();
            }
        };

        // 自动续接逻辑
        if (localStorage.getItem('gs_is_running') === 'true') {
            setTimeout(runAuto, 1500);
        }
    };

    // 启动
    if (document.readyState === 'complete') init();
    else window.addEventListener('load', init);

})();