// ==UserScript== // @name Google Search Suggestions Collector // @namespace http://tampermonkey.net/ // @version 0.1 // @description Collect Google search suggestions // @author WWW // @include *://www.google.*/* // @include *://google.*/* // @grant GM_setClipboard // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 在全局作用域内添加状态变量 let isCollecting = false; let shouldStop = false; function addStyles() { const style = document.createElement('style'); style.textContent = ` .suggest-collector-btn { position: fixed; right: 200px; top: 20px; width: 50px; height: 50px; border-radius: 25px; background: var(--collector-bg, #ffffff); border: 2px solid var(--collector-border, #e0e0e0); box-shadow: 0 2px 12px rgba(0,0,0,0.15); cursor: move; z-index: 10000; display: flex; align-items: center; justify-content: center; user-select: none; } .suggest-collector-panel { position: fixed; width: 300px; background: var(--collector-bg, #ffffff); border: 1px solid var(--collector-border, #e0e0e0); border-radius: 8px; padding: 15px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); z-index: 9999; display: none; } .suggest-collector-panel input { width: 100%; padding: 8px; border: 1px solid var(--collector-border, #e0e0e0); border-radius: 4px; margin-bottom: 10px; background: var(--collector-input-bg, #ffffff); color: var(--collector-text, #333333); } .suggest-collector-panel button { padding: 8px 16px; border: none; border-radius: 4px; background: #4CAF50; color: white; cursor: pointer; transition: background 0.3s; } .suggest-collector-panel button:hover { background: #45a049; } .suggest-collector-panel textarea { background: var(--collector-input-bg, #ffffff); color: var(--collector-text, #333333); border: 1px solid var(--collector-border, #e0e0e0); border-radius: 4px; } @media (prefers-color-scheme: dark) { :root { --collector-bg: #2d2d2d; --collector-border: #404040; --collector-text: #e0e0e0; --collector-input-bg: #3d3d3d; } } .input-mode-selector { display: flex; gap: 20px; margin-bottom: 15px; padding: 0 10px; } .input-mode-selector label { display: flex; align-items: center; gap: 5px; cursor: pointer; color: var(--collector-text, #333333); min-width: 70px; } .input-mode-selector input[type="radio"], .filter-options input[type="checkbox"] { margin: 0; cursor: pointer; width: 16px; height: 16px; } .filter-options { margin-bottom: 15px; padding: 0 10px; } .filter-options label { display: flex; align-items: center; gap: 5px; cursor: pointer; color: var(--collector-text, #333333); justify-content: flex-end; } #singleInput { padding: 0 10px; } .depth-selector { margin-bottom: 15px; padding: 0 10px; display: flex; align-items: center; gap: 10px; } .depth-selector label { color: var(--collector-text, #333333); } .depth-selector select { padding: 5px; border-radius: 4px; border: 1px solid var(--collector-border, #e0e0e0); background: var(--collector-input-bg, #ffffff); color: var(--collector-text, #333333); cursor: pointer; } `; document.head.appendChild(style); } function createUI() { const btn = document.createElement('div'); btn.className = 'suggest-collector-btn'; btn.innerHTML = '🔍'; document.body.appendChild(btn); const panel = document.createElement('div'); panel.className = 'suggest-collector-panel'; panel.innerHTML = `
`; document.body.appendChild(panel); let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; // 更新面板位置的函数 function updatePanelPosition() { const btnRect = btn.getBoundingClientRect(); panel.style.right = `${window.innerWidth - (btnRect.right + 20)}px`; panel.style.top = `${btnRect.bottom + 20}px`; } btn.addEventListener('mousedown', dragStart); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); function dragStart(e) { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.target === btn) { isDragging = true; } } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; btn.style.transform = `translate(${currentX}px, ${currentY}px)`; // 拖动时更新面板位置 updatePanelPosition(); } } function dragEnd() { isDragging = false; } btn.addEventListener('click', (e) => { if (!isDragging) { panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; if (panel.style.display === 'block') { updatePanelPosition(); } } }); const radioButtons = panel.querySelectorAll('input[name="inputMode"]'); radioButtons.forEach(radio => { radio.addEventListener('change', (e) => { document.getElementById('singleInput').style.display = e.target.value === 'single' ? 'block' : 'none'; document.getElementById('batchInput').style.display = e.target.value === 'batch' ? 'block' : 'none'; }); }); } async function getSuggestions(keyword) { const response = await fetch(`https://suggestqueries.google.com/complete/search?client=chrome&q=${encodeURIComponent(keyword)}`); const data = await response.json(); return data[1]; } function updateProgress(current, total, collectedItems) { const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); const collectedCount = document.getElementById('collectedCount'); const progress = document.getElementById('progress'); progress.style.display = 'block'; const percentage = (current / total) * 100; progressBar.style.width = percentage + '%'; progressText.textContent = `${current}/${total}`; collectedCount.textContent = collectedItems.size; } function generateCombinations(letters, depth) { if (depth === 1) return letters.map(letter => [letter]); const combinations = []; for (let i = 0; i < letters.length; i++) { const subCombinations = generateCombinations(letters.slice(i + 1), depth - 1); subCombinations.forEach(subComb => { combinations.push([letters[i], ...subComb]); }); } return combinations; } async function collectSuggestions(baseKeyword) { const result = new Set(); const letters = 'abcdefghijklmnopqrstuvwxyz'.split(''); const resultDiv = document.getElementById('result'); const onlyEnglish = document.getElementById('onlyEnglish').checked; const searchDepth = parseInt(document.getElementById('searchDepth').value); const isEnglishOnly = (text) => /^[A-Za-z0-9\s.,!?-]+$/.test(text); if (shouldStop) { return Array.from(result); } // 收集基础关键词的建议 const baseSuggestions = await getSuggestions(baseKeyword); baseSuggestions.forEach(s => { if (!onlyEnglish || isEnglishOnly(s)) { result.add(s); } }); // 生成所有可能的字母组合 const allCombinations = []; for (let depth = 1; depth <= searchDepth; depth++) { const depthCombinations = generateCombinations(letters, depth); allCombinations.push(...depthCombinations); } // 更新进度条的总数 const totalCombinations = allCombinations.length; updateProgress(0, totalCombinations, result); // 对每个组合进行查询 for (let i = 0; i < allCombinations.length; i++) { if (shouldStop) { break; } const combination = allCombinations[i]; const letterCombination = combination.join(''); const suggestions = await getSuggestions(`${baseKeyword} ${letterCombination}`); suggestions.forEach(s => { if (!onlyEnglish || isEnglishOnly(s)) { result.add(s); } }); updateProgress(i + 1, totalCombinations, result); resultDiv.innerHTML = ``; await new Promise(resolve => setTimeout(resolve, 200)); } return Array.from(result); } function init() { addStyles(); createUI(); const startCollectBtn = document.getElementById('startCollect'); startCollectBtn.addEventListener('click', async () => { if (isCollecting) { // 如果正在收集,点击按钮则停止 shouldStop = true; startCollectBtn.textContent = 'start collect'; startCollectBtn.style.background = '#4CAF50'; isCollecting = false; return; } const isBatchMode = document.querySelector('input[name="inputMode"]:checked').value === 'batch'; let keywords = []; if (isBatchMode) { const batchText = document.getElementById('batchKeywords').value.trim(); keywords = batchText.split('\n').filter(k => k.trim()); } else { const singleKeyword = document.getElementById('baseKeyword').value.trim(); if (singleKeyword) { keywords = [singleKeyword]; } } if (keywords.length === 0) { alert('Please enter a keyword'); return; } // 开始收集 isCollecting = true; shouldStop = false; startCollectBtn.textContent = 'stop collect'; startCollectBtn.style.background = '#ff4444'; const resultDiv = document.getElementById('result'); resultDiv.innerHTML = 'Collecting...'; document.getElementById('progress').style.display = 'block'; try { const allSuggestions = new Set(); const totalKeywords = keywords.length; for (let i = 0; i < keywords.length; i++) { if (shouldStop) { break; } const keyword = keywords[i]; document.getElementById('totalProgress').textContent = `${i + 1}/${totalKeywords}`; document.getElementById('totalProgressBar').style.width = `${((i + 1) / totalKeywords) * 100}%`; const suggestions = await collectSuggestions(keyword); suggestions.forEach(s => allSuggestions.add(s)); } const resultText = Array.from(allSuggestions).join('\n'); resultDiv.innerHTML = ` `; document.getElementById('copyBtn').addEventListener('click', () => { GM_setClipboard(resultText); alert('Copied to clipboard!'); }); } catch (error) { resultDiv.innerHTML = 'Error occurred while collecting: ' + error.message; } finally { // 恢复按钮状态 isCollecting = false; shouldStop = false; startCollectBtn.textContent = 'start collect'; startCollectBtn.style.background = '#4CAF50'; } }); } init(); })();