Google Search Result Extractor

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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);

})();