Google Search Result Extractor

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

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

})();