您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Track and统计 TikTok Library 广告主数据,支持localStorage持久化、数据清除、详情页信息提取
// ==UserScript== // @name TikTok Library Advertiser Tracker - 增强版 // @namespace weixin:eva_mirror // @version 1.0 // @description Track and统计 TikTok Library 广告主数据,支持localStorage持久化、数据清除、详情页信息提取 // @author Sheire // @match https://library.tiktok.com/* // @grant none // ==/UserScript== (function() { 'use strict'; // 存储广告主数据 let advertiserData = new Map(); // 存储广告主对应的biz_ids映射 let advertiserBizIds = new Map(); // localStorage键名 const STORAGE_KEY = 'tiktok_advertiser_data'; const BIZ_IDS_STORAGE_KEY = 'tiktok_advertiser_biz_ids'; // 从localStorage加载数据 function loadFromStorage() { try { const savedData = localStorage.getItem(STORAGE_KEY); if (savedData) { const parsedData = JSON.parse(savedData); advertiserData = new Map(); for (const [name, idsArray] of Object.entries(parsedData)) { advertiserData.set(name, new Set(idsArray)); } } const savedBizIds = localStorage.getItem(BIZ_IDS_STORAGE_KEY); if (savedBizIds) { advertiserBizIds = new Map(JSON.parse(savedBizIds)); } } catch (e) { console.error('从localStorage加载数据失败:', e); } } // 保存数据到localStorage function saveToStorage() { try { // 将Map转换为可序列化的对象 const serializableData = {}; advertiserData.forEach((ids, name) => { serializableData[name] = Array.from(ids); }); localStorage.setItem(STORAGE_KEY, JSON.stringify(serializableData)); // 保存biz_ids映射 localStorage.setItem(BIZ_IDS_STORAGE_KEY, JSON.stringify(Array.from(advertiserBizIds.entries()))); } catch (e) { console.error('保存数据到localStorage失败:', e); } } // 创建悬浮按钮 function createFloatingButton() { // 如果按钮已存在,先移除 const existingButton = document.getElementById('advertiser-tracker-btn'); if (existingButton) { existingButton.remove(); } const button = document.createElement('div'); button.id = 'advertiser-tracker-btn'; button.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 300px; height: 50px; background-color: orange; color: white; border-radius: 5px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 9999; font-weight: bold; box-shadow: 0 2px 10px rgba(0,0,0,0.3); `; button.textContent = '查看广告主 (0)'; document.body.appendChild(button); button.addEventListener('click', showAdvertiserModal); console.log('TikTok Advertiser Tracker: 悬浮按钮已创建'); } // 更新按钮文本 function updateButtonText() { const button = document.getElementById('advertiser-tracker-btn'); if (button) { button.textContent = `查看广告主 (${advertiserData.size})`; } // 每次更新数据时保存到localStorage saveToStorage(); } // 显示广告主统计弹窗 function showAdvertiserModal() { // 每次打开弹窗时都获取localStorage最新的数据 loadFromStorage(); // 如果模态框已存在,先移除 const existingModal = document.getElementById('advertiser-modal'); if (existingModal) { existingModal.remove(); } const modal = document.createElement('div'); modal.id = 'advertiser-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.7); z-index: 10000; display: flex; justify-content: center; align-items: center; `; const modalContent = document.createElement('div'); modalContent.style.cssText = ` background-color: white; border-radius: 10px; width: 80%; max-width: 800px; max-height: 80%; overflow: auto; padding: 20px; `; const header = document.createElement('div'); header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; position: sticky; top: 0; background-color: white; z-index: 100; `; const title = document.createElement('h2'); title.textContent = '广告主统计'; const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = ` display: flex; gap: 10px; `; const clearButton = document.createElement('button'); clearButton.textContent = '清除数据'; clearButton.style.cssText = ` padding: 5px 15px; background-color: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer; `; clearButton.onclick = clearAllData; const closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.cssText = ` padding: 5px 15px; background-color: #ccc; border: none; border-radius: 3px; cursor: pointer; `; closeButton.onclick = () => modal.remove(); buttonContainer.appendChild(clearButton); buttonContainer.appendChild(closeButton); header.appendChild(title); header.appendChild(buttonContainer); const table = document.createElement('table'); table.style.cssText = ` width: 100%; border-collapse: collapse; `; const tableHeader = document.createElement('thead'); tableHeader.innerHTML = ` <tr> <th style="border: 1px solid #ddd; padding: 8px; text-align: left;">广告主名称</th> <th style="border: 1px solid #ddd; padding: 8px; text-align: left;">广告数量</th> <th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Biz ID</th> </tr> `; const tableBody = document.createElement('tbody'); // 按广告主名称统计并排序 const stats = []; advertiserData.forEach((ids, name) => { const bizId = advertiserBizIds.get(name) || '-'; stats.push({name, count: ids.size, bizId}); }); // 排序:第一排序,有Biz ID的排在前面;第二排序,按广告数量降序 stats.sort((a, b) => { // 检查是否有Biz ID(不是'-'表示有Biz ID) const aHasBizId = a.bizId !== '-'; const bHasBizId = b.bizId !== '-'; // 如果一个有Biz ID,一个没有,则有Biz ID的排在前面 if (aHasBizId && !bHasBizId) return -1; if (!aHasBizId && bHasBizId) return 1; // 如果都有或都没有Biz ID,则按广告数量降序排序 return b.count - a.count; }); // 填充表格数据 stats.forEach(({name, count, bizId}) => { const row = document.createElement('tr'); row.innerHTML = ` <td style="border: 1px solid #ddd; padding: 8px;">${name}</td> <td style="border: 1px solid #ddd; padding: 8px;">${count}</td> <td style="border: 1px solid #ddd; padding: 8px;">${bizId}</td> `; tableBody.appendChild(row); }); table.appendChild(tableHeader); table.appendChild(tableBody); modalContent.appendChild(header); modalContent.appendChild(table); modal.appendChild(modalContent); document.body.appendChild(modal); // 点击背景关闭模态框 modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); } // 清除所有数据 function clearAllData() { if (confirm('确定要清除所有广告主数据吗?')) { advertiserData.clear(); advertiserBizIds.clear(); updateButtonText(); saveToStorage(); alert('数据已清除'); } } // 处理响应数据的函数 function processResponseData(data) { if (data && data.data && Array.isArray(data.data)) { let processedCount = 0; data.data.forEach(item => { const { name, id } = item; if (name && id) { if (!advertiserData.has(name)) { advertiserData.set(name, new Set()); } if (!advertiserData.get(name).has(id)) { advertiserData.get(name).add(id); processedCount++; } } }); if (processedCount > 0) { updateButtonText(); } } } // 从详情页提取广告主信息 function extractAdvertiserInfoFromDetailPage() { // 检查是否是详情页 if (window.location.pathname === '/ads/detail/' && window.location.search) { console.log('检测到详情页,尝试提取广告主信息'); // 查找广告主名称 const advertiserNameElement = document.querySelector('.ad_advertiser_value'); if (advertiserNameElement) { const advertiserName = advertiserNameElement.textContent.trim(); console.log('找到广告主名称:', advertiserName); // 查找包含biz_id的链接 const linkElement = document.querySelector('a.item_link[href*="adv_biz_ids"]'); if (linkElement) { const href = linkElement.getAttribute('href'); console.log('找到链接:', href); const urlParams = new URLSearchParams(href.split('?')[1]); const bizId = urlParams.get('adv_biz_ids'); console.log('提取到Biz ID:', bizId); if (advertiserName && bizId) { // 保存映射关系 advertiserBizIds.set(advertiserName, bizId); saveToStorage(); console.log(`提取到广告主信息: ${advertiserName} -> ${bizId}`); } } else { console.log('未找到包含biz_id的链接'); } } else { console.log('未找到广告主名称元素'); } } } // 拦截 fetch 请求 const originalFetch = window.fetch; window.fetch = function(...args) { const [url, options] = args; // 监听搜索API if (typeof url === 'string' && url.includes('/api/v1/search')) { return originalFetch.apply(this, args).then(response => { // 克隆响应以便可以多次读取 const clone = response.clone(); // 读取响应内容 clone.json().then(data => { processResponseData(data); }).catch(e => { console.error('解析响应数据时出错:', e); }); return response; }).catch(e => { console.error('fetch 请求出错:', e); throw e; }); } // 非搜索API请求,正常处理 return originalFetch.apply(this, args); }; // 同时拦截 XMLHttpRequest const originalXhrOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { if (typeof url === 'string' && url.includes('/api/v1/search')) { this.addEventListener('load', function() { if (this.status === 200) { try { const data = JSON.parse(this.responseText); processResponseData(data); } catch (e) { console.error('XMLHttpRequest 解析响应数据时出错:', e); } } }); } originalXhrOpen.apply(this, arguments); }; // 初始化 window.addEventListener('load', () => { // 从localStorage加载数据 loadFromStorage(); createFloatingButton(); updateButtonText(); // 尝试从详情页提取信息 setTimeout(extractAdvertiserInfoFromDetailPage, 2000); console.log('TikTok Advertiser Tracker: 脚本初始化完成'); }); console.log('TikTok Advertiser Tracker: 脚本已注入'); })();