您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动收集title和url,过滤重复域名,翻页时重置
// ==UserScript== // @name Google 搜索结果自动收集 (带开关) // @namespace http://tampermonkey.net/ // @version 2025-07-22 // @description 自动收集title和url,过滤重复域名,翻页时重置 // @author AI // @@version 1.2 // @match https://www.google.com/*search* // @match https://www.google.com.hk/*search* // @icon https://www.google.com/s2/favicons?sz=64&domain=google.com // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // --- Storage Keys --- const STORAGE_KEY_RESULTS = 'googleCopier_sessionResults'; const STORAGE_KEY_SEEN_ITEMS = 'googleCopier_sessionSeenItems'; // Can hold URLs or Hostnames const STORAGE_KEY_QUERY = 'googleCopier_sessionQuery'; const STORAGE_KEY_FILTER_DOMAIN = 'googleCopier_filterDomainEnabled'; const STORAGE_KEY_RESET_ON_NEXT = 'googleCopier_resetOnNextEnabled'; // --- Global State --- let filterByDomainEnabled = GM_getValue(STORAGE_KEY_FILTER_DOMAIN, false); let resetOnNextEnabled = GM_getValue(STORAGE_KEY_RESET_ON_NEXT, false); let currentData = {}; // Will be initialized by loadData // --- Session Management --- (function handleSession() { const params = new URLSearchParams(window.location.search); const currentQuery = params.get('q') || ''; const storedQuery = GM_getValue(STORAGE_KEY_QUERY, null); if (currentQuery !== storedQuery) { console.log(`新搜索已启动。旧关键字: '${storedQuery}', 新关键字: '${currentQuery}'。正在清空旧会话数据。`); GM_deleteValue(STORAGE_KEY_RESULTS); GM_deleteValue(STORAGE_KEY_SEEN_ITEMS); GM_setValue(STORAGE_KEY_QUERY, currentQuery); } })(); // --- Data Management --- function loadData() { let results = GM_getValue(STORAGE_KEY_RESULTS, []); let seenItems = new Set(GM_getValue(STORAGE_KEY_SEEN_ITEMS, [])); if (!Array.isArray(results)) { results = []; seenItems = new Set(); } currentData = { allUniqueResults: results, seenItems: seenItems }; } function saveData() { GM_setValue(STORAGE_KEY_RESULTS, currentData.allUniqueResults); GM_setValue(STORAGE_KEY_SEEN_ITEMS, Array.from(currentData.seenItems)); } function resetData() { console.log("已收集的数据已清空。"); GM_deleteValue(STORAGE_KEY_RESULTS); GM_deleteValue(STORAGE_KEY_SEEN_ITEMS); loadData(); // Reload empty data structures updateButtonCounts(0); } // --- Core Logic --- function getGoogleSearchResultsAndStoreUniques() { loadData(); // Always load the latest data from storage before collecting let newResultsThisPageCount = 0; const resultLinkElements = Array.from(document.querySelectorAll('#rso a[href]')) .filter(a => a.querySelector('h3') && a.href && !a.href.startsWith('javascript:')); resultLinkElements.forEach(linkElement => { const h3 = linkElement.querySelector('h3'); if (h3) { const title = h3.innerText.trim(); const url = linkElement.href; const hostname = new URL(url).hostname.replace(/^www\./, ''); const itemToCheck = filterByDomainEnabled ? hostname : url; if (title && url && !currentData.seenItems.has(itemToCheck)) { currentData.allUniqueResults.push({ title, url, hostname }); currentData.seenItems.add(itemToCheck); newResultsThisPageCount++; } } }); if (newResultsThisPageCount > 0) { console.log(`当前页新增 ${newResultsThisPageCount} 条唯一结果。总共已收集 ${currentData.allUniqueResults.length} 条。`); } else { console.log(`当前页未找到新的唯一结果。总共已收集 ${currentData.allUniqueResults.length} 条。`); } saveData(); updateButtonCounts(currentData.allUniqueResults.length); } function clickNextPage() { const nextPageButton = document.getElementById('pnnext') || document.querySelector('a[aria-label="下一页"]'); if (nextPageButton && nextPageButton.href) { nextPageButton.click(); } else { console.warn("未找到“下一页”按钮,或已到达最后一页。"); } } // --- UI Elements --- let copyAllButtonGlobal, openUrlsButtonGlobal; function updateButtonCounts(count) { if (copyAllButtonGlobal) copyAllButtonGlobal.textContent = `复制所有 (${count}) 结果`; if (openUrlsButtonGlobal) openUrlsButtonGlobal.textContent = `批量打开 (${count}) 链接`; } function createCheckbox(id, labelText, isChecked, onChangeCallback) { const container = document.createElement('div'); Object.assign(container.style, { display: 'flex', alignItems: 'center', gap: '5px' }); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = id; checkbox.checked = isChecked; checkbox.onchange = onChangeCallback; Object.assign(checkbox.style, { margin: '0', cursor: 'pointer' }); const label = document.createElement('label'); label.htmlFor = id; label.textContent = labelText; Object.assign(label.style, { fontSize: '13px', color: '#333', cursor: 'pointer', userSelect: 'none' }); container.appendChild(checkbox); container.appendChild(label); return container; } function createControlButtons() { if (document.getElementById('googleResultCopierPanel_GM')) return; const panel = document.createElement('div'); panel.id = 'googleResultCopierPanel_GM'; Object.assign(panel.style, { position: 'fixed', top: '20px', right: '20px', zIndex: '2147483647', padding: '12px', backgroundColor: 'rgba(240, 240, 240, 0.95)', border: '1px solid #bbb', borderRadius: '8px', boxShadow: '0 4px 8px rgba(0,0,0,0.2)', display: 'flex', flexDirection: 'column', gap: '10px' }); const titleBar = document.createElement('div'); titleBar.textContent = '结果收集器'; Object.assign(titleBar.style, { fontWeight: 'bold', textAlign: 'center', paddingBottom: '8px', borderBottom: '1px solid #ddd' }); panel.appendChild(titleBar); const filterCheckbox = createCheckbox('filterDomainCheckbox', '过滤重复域名', filterByDomainEnabled, (e) => { filterByDomainEnabled = e.target.checked; GM_setValue(STORAGE_KEY_FILTER_DOMAIN, filterByDomainEnabled); console.log(`过滤重复域名已'${filterByDomainEnabled ? '开启' : '关闭'}'。正在重置并重新收集当前页...`); resetData(); getGoogleSearchResultsAndStoreUniques(); }); panel.appendChild(filterCheckbox); const resetCheckbox = createCheckbox('resetOnNextCheckbox', '翻页时重置', resetOnNextEnabled, (e) => { resetOnNextEnabled = e.target.checked; GM_setValue(STORAGE_KEY_RESET_ON_NEXT, resetOnNextEnabled); console.log(`翻页时重置已'${resetOnNextEnabled ? '开启' : '关闭'}'。`); }); panel.appendChild(resetCheckbox); const buttonContainer = document.createElement('div'); Object.assign(buttonContainer.style, { display: 'flex', flexDirection: 'column', gap: '8px', paddingTop: '8px', borderTop: '1px solid #ddd' }); panel.appendChild(buttonContainer); const buttonConfigs = [ { text: `复制所有 (0) 结果`, color: '#008CBA', action: () => { if (currentData.allUniqueResults.length === 0) { console.log("没有结果可复制。"); return; } const excelFormattedText = "Title\tURL\n" + currentData.allUniqueResults.map(r => `${r.title.replace(/[\n\t]/g, ' ')}\t${r.url}`).join('\n'); navigator.clipboard.writeText(excelFormattedText.trim()).then(() => console.log('复制成功!')).catch(err => console.error('复制失败:', err)); }, ref: (btn) => { copyAllButtonGlobal = btn; } }, { text: `批量打开 (0) 链接`, color: '#9C27B0', action: () => { if (currentData.allUniqueResults.length === 0) { console.log("没有链接可打开。"); return; } currentData.allUniqueResults.forEach((r, i) => setTimeout(() => window.open(r.url, '_blank'), i * 150)); }, ref: (btn) => { openUrlsButtonGlobal = btn; } }, // ***** THIS IS THE CORRECTED LOGIC BLOCK ***** { text: '前往下一页', color: '#f44336', action: () => { if (resetOnNextEnabled) { // If reset is enabled, just clear the data and navigate. // The script will automatically collect on the new page. console.log("翻页时重置已启用,正在清空数据并前往下一页..."); resetData(); clickNextPage(); } else { // If reset is NOT enabled, collect the current page's data first, then navigate. console.log("正在收集当前页并前往下一页..."); getGoogleSearchResultsAndStoreUniques(); setTimeout(clickNextPage, 100); } }}, { text: '重置当前会话数据', color: '#607D8B', action: () => resetData() } ]; buttonConfigs.forEach(config => { const button = document.createElement('button'); button.textContent = config.text; Object.assign(button.style, { padding: '10px 15px', backgroundColor: config.color, color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', fontSize: '14px', textAlign: 'center', transition: 'background-color 0.2s ease' }); button.onmouseover = () => { button.style.opacity = '0.9'; }; button.onmouseout = () => { button.style.opacity = '1'; }; button.onclick = config.action; if (config.ref) config.ref(button); buttonContainer.appendChild(button); }); document.body.appendChild(panel); // Load data initially to populate UI loadData(); updateButtonCounts(currentData.allUniqueResults.length); console.log("Tampermonkey: 自动结果收集器面板已创建。"); } // --- Script Entry Point --- const checkInterval = setInterval(() => { if (document.getElementById('rso') || document.getElementById('search')) { clearInterval(checkInterval); console.log("Google 结果容器已找到,开始自动收集..."); createControlButtons(); // Create UI first getGoogleSearchResultsAndStoreUniques(); // Then collect } }, 500); })();