您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
获取Google广告透明度中心的搜索推荐数据
// ==UserScript== // @name Google Ads Transparency Scraper // @namespace 微信:eva-mirror // @version 1.0 // @description 获取Google广告透明度中心的搜索推荐数据 // @author sheire // @match https://adstransparency.google.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect adstransparency.google.com // ==/UserScript== (function() { 'use strict'; // 存储拦截到的数据 let interceptedData = []; let isPanelOpen = false; // 添加自定义样式 GM_addStyle(` #fetch-recommendations-btn { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); width: 300px; height: 40px; background-color: #4285f4; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; z-index: 10000; box-shadow: 0 2px 10px rgba(0,0,0,0.2); } #fetch-recommendations-btn:hover { background-color: #3367d6; } #data-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-height: 80%; background: white; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10001; display: none; flex-direction: column; overflow: hidden; } #data-panel-header { padding: 16px; background: #f5f5f5; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #ddd; } #data-panel-content { padding: 16px; overflow-y: auto; flex-grow: 1; } #close-panel { background: none; border: none; font-size: 24px; cursor: pointer; color: #757575; } #copy-data, #copy-selected-data { background: #4285f4; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-bottom: 16px; margin-right: 10px; } #copy-data:hover, #copy-selected-data:hover { background: #3367d6; } #data-table { width: 100%; border-collapse: collapse; } #data-table th, #data-table td { border: 1px solid #ddd; padding: 8px; text-align: left; } #data-table th { background-color: #f2f2f2; position: sticky; top: 0; } #data-table th:first-child, #data-table td:first-child { width: 40px; text-align: center; } #overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10000; display: none; } .select-all-container { margin-bottom: 10px; } .select-all-container label { font-weight: bold; cursor: pointer; } `); // 创建按钮 const button = document.createElement('button'); button.id = 'fetch-recommendations-btn'; button.textContent = '获取搜索推荐 (0)'; document.body.appendChild(button); // 创建弹窗和遮罩 const overlay = document.createElement('div'); overlay.id = 'overlay'; const panel = document.createElement('div'); panel.id = 'data-panel'; panel.innerHTML = ` <div id="data-panel-header"> <h3>搜索推荐数据</h3> <button id="close-panel">×</button> </div> <div id="data-panel-content"> <button id="copy-data">一键复制全部内容</button> <button id="copy-selected-data">复制勾选</button> <div class="select-all-container"> <label> <input type="checkbox" id="select-all-checkbox"> 全选 </label> </div> <table id="data-table"> <thead> <tr> <th><input type="checkbox" id="select-all-header"></th> <th>广告主名</th> <th>资料库 Id or 域名</th> <th>地区</th> </tr> </thead> <tbody></tbody> </table> </div> `; document.body.appendChild(overlay); document.body.appendChild(panel); // 监听按钮点击事件 button.addEventListener('click', () => { showPanel(); }); // 关闭弹窗事件 document.getElementById('close-panel').addEventListener('click', () => { hidePanel(); }); // 点击遮罩关闭弹窗 overlay.addEventListener('click', () => { hidePanel(); }); // 复制数据事件 document.getElementById('copy-data').addEventListener('click', () => { copyAllDataToClipboard(); }); // 复制选中数据事件 document.getElementById('copy-selected-data').addEventListener('click', () => { copySelectedDataToClipboard(); }); // 全选复选框事件 document.getElementById('select-all-header').addEventListener('change', function() { const checkboxes = document.querySelectorAll('.row-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = this.checked; }); }); document.getElementById('select-all-checkbox').addEventListener('change', function() { const checkboxes = document.querySelectorAll('.row-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = this.checked; }); document.getElementById('select-all-header').checked = this.checked; }); // 显示弹窗 function showPanel() { overlay.style.display = 'block'; panel.style.display = 'flex'; isPanelOpen = true; renderTable(); } // 隐藏弹窗 function hidePanel() { overlay.style.display = 'none'; panel.style.display = 'none'; isPanelOpen = false; } // 渲染表格数据 function renderTable() { const tbody = document.querySelector('#data-table tbody'); tbody.innerHTML = ''; interceptedData.forEach((item, index) => { const row = document.createElement('tr'); row.innerHTML = ` <td><input type="checkbox" class="row-checkbox" data-index="${index}"></td> <td>${item['1'] || ''}</td> <td>${item['2'] || ''}</td> <td>${item['3'] || ''}</td> `; tbody.appendChild(row); }); // 为新添加的复选框添加事件监听器 document.querySelectorAll('.row-checkbox').forEach(checkbox => { checkbox.addEventListener('change', function() { // 检查是否所有行都被选中,以更新全选复选框状态 const allCheckboxes = document.querySelectorAll('.row-checkbox'); const allChecked = Array.from(allCheckboxes).every(cb => cb.checked); document.getElementById('select-all-header').checked = allChecked; document.getElementById('select-all-checkbox').checked = allChecked; }); }); } // 复制全部数据到剪贴板 function copyAllDataToClipboard() { let csvContent = "广告主名,资料库 Id or 域名,地区\n"; interceptedData.forEach(item => { csvContent += `"${item['1'] || ''}","${item['2'] || ''}","${item['3'] || ''}"\n`; }); navigator.clipboard.writeText(csvContent).then(() => { alert('数据已复制到剪贴板'); }).catch(err => { console.error('复制失败:', err); alert('复制失败'); }); } // 复制选中数据到剪贴板 function copySelectedDataToClipboard() { const selectedCheckboxes = document.querySelectorAll('.row-checkbox:checked'); if (selectedCheckboxes.length === 0) { alert('请至少选择一项'); return; } const selectedIds = Array.from(selectedCheckboxes).map(checkbox => { const index = parseInt(checkbox.getAttribute('data-index')); return interceptedData[index]['2'] || ''; }).filter(id => id !== ''); const clipboardText = selectedIds.join(','); navigator.clipboard.writeText(clipboardText).then(() => { alert(`已复制 ${selectedIds.length} 个资料库 Id or 域名到剪贴板`); }).catch(err => { console.error('复制失败:', err); alert('复制失败'); }); } // 更新按钮文本 function updateButtonText() { button.textContent = `获取搜索推荐 (${interceptedData.length})`; } // 解析不同类型的数据格式 function parseDataItem(item) { // 类型1: {"1": {"1": "广告主名", "2": "资料库Id", "3": "地区"}} if (item['1']) { return { '1': item['1']['1'] || '', '2': item['1']['2'] || '', '3': item['1']['3'] || '' }; } // 类型2: {"2": {"1": "域名"}} if (item['2']) { return { '1': '', // 广告主名未知 '2': item['2']['1'] || '', // 域名 '3': '' // 地区未知 }; } return null; } // 检查并处理响应数据 function processResponseData(data) { try { if (typeof data === 'string') { data = JSON.parse(data); } let parsedItems = []; // 处理主要数据结构 if (data && data['1'] && Array.isArray(data['1'])) { data['1'].forEach(item => { const parsed = parseDataItem(item); if (parsed) { parsedItems.push(parsed); } }); } // 处理其他可能的数据结构 if (data && data['2'] && Array.isArray(data['2'])) { data['2'].forEach(item => { const parsed = parseDataItem(item); if (parsed) { parsedItems.push(parsed); } }); } // 如果直接是对象数组 if (data && Array.isArray(data)) { data.forEach(item => { const parsed = parseDataItem(item); if (parsed) { parsedItems.push(parsed); } }); } // 添加新解析的数据 let newDataCount = 0; parsedItems.forEach(parsedItem => { // 检查是否已存在相同数据 const exists = interceptedData.some(existing => existing['2'] === parsedItem['2'] && existing['2'] !== '' ); if (!exists) { interceptedData.push(parsedItem); newDataCount++; } }); if (parsedItems.length > 0) { updateButtonText(); // 如果面板打开中,更新表格 if (isPanelOpen) { renderTable(); } } } catch (err) { console.error('处理数据出错:', err); } } // 方法1: 拦截 fetch 请求 const originalFetch = window.fetch; window.fetch = function(...args) { const [resource, options] = args; // 检查是否为 SearchSuggestions 请求 if (typeof resource === 'string' && resource.includes('/SearchService/SearchSuggestions')) { return originalFetch(...args).then(response => { // 克隆响应以便可以读取内容 const clonedResponse = response.clone(); clonedResponse.text().then(text => { processResponseData(text); }).catch(err => { console.error('读取fetch响应失败:', err); }); return response; }).catch(err => { console.error('fetch请求失败:', err); throw err; }); } return originalFetch(...args); }; // 方法2: 拦截 XMLHttpRequest 请求 const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { if (url && url.includes('/SearchService/SearchSuggestions')) { this.addEventListener('load', function() { try { processResponseData(this.responseText); } catch (err) { console.error('处理XMLHttpRequest响应失败:', err); } }); } originalOpen.apply(this, arguments); }; // 方法3: 定期检查页面数据(备用方案) setInterval(() => { try { // 尝试从页面中查找相关数据 const scripts = document.querySelectorAll('script'); scripts.forEach(script => { if (script.textContent && script.textContent.includes('SearchSuggestions')) { // 尝试从script标签中提取JSON数据 const matches = script.textContent.match(/\{[^{]*?"[12]".*?\}/gs); if (matches) { matches.forEach(match => { try { const data = JSON.parse(match); if (data['1'] || data['2']) { processResponseData(data); } } catch (e) { // 忽略解析错误 } }); } } }); } catch (e) { // 忽略错误 } }, 3000); })();