您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在小红书创作者后台的笔记管理页面提取并展示指标数据(浏览量、评论数、点赞数、收藏数、转发数)。
当前为
// ==UserScript== // @name Metrics Extractor for Xiaohongshu Note Manager // @namespace http://tampermonkey.net/ // @version 1.7 // @description 在小红书创作者后台的笔记管理页面提取并展示指标数据(浏览量、评论数、点赞数、收藏数、转发数)。 // @author 您的名字 // @match https://creator.xiaohongshu.com/new/note-manager* // @grant none // ==/UserScript== (function() { 'use strict'; // 防止脚本多次运行 if (document.getElementById('metrics-extractor-container')) { console.log('Metrics Extractor 已经运行,退出脚本。'); return; } const metrics = ['浏览量', '评论数', '点赞数', '收藏数', '转发数']; let categorizedData = []; let totals = { '浏览量': 0, '评论数': 0, '点赞数': 0, '收藏数': 0, '转发数': 0 }; /** * 提取指标数据 * @returns {boolean} 是否成功提取数据 */ function extractMetrics() { console.log('开始提取指标数据...'); // 使用更通用的选择器 const iconDivs = document.querySelectorAll('div.icon, div[class*="icon"]'); console.log('找到的div.icon数量:', iconDivs.length); const numbers = []; iconDivs.forEach(div => { const span = div.querySelector('span'); if (span) { const numberText = span.textContent.trim(); console.log(`提取到的span文本: "${numberText}"`); // 移除非数字字符(如逗号、空格等) const cleanedNumber = numberText.replace(/[^\d]/g, ''); const number = parseInt(cleanedNumber, 10); if (!isNaN(number)) { numbers.push(number); } else { console.warn(`无法解析数字: "${numberText}"`); } } else { console.warn('未找到span元素'); } }); console.log('提取到的数字:', numbers); if (numbers.length === 0) { console.warn('未提取到任何数字。'); return false; } if (numbers.length % 5 !== 0) { console.warn('提取的数字数量不是5的倍数,可能存在数据缺失或多余。'); } categorizedData = []; for (let i = 0; i < numbers.length; i += 5) { const item = {}; metrics.forEach((metric, index) => { if (i + index < numbers.length) { item[metric] = numbers[i + index]; } else { item[metric] = null; } }); categorizedData.push(item); } console.log('分类后的数据:', categorizedData); // 计算总数 totals = { '浏览量': 0, '评论数': 0, '点赞数': 0, '收藏数': 0, '转发数': 0 }; categorizedData.forEach(item => { metrics.forEach(metric => { const value = item[metric]; if (typeof value === 'number') { totals[metric] += value; } }); }); console.log('各指标总数:', totals); // 创建或更新面板 if (document.getElementById('metrics-extractor-container')) { updateMetricsPanel(); } else { createMetricsPanel(); } return true; } /** * 创建指标展示面板 */ function createMetricsPanel() { console.log('创建指标面板...'); // 创建样式 const style = document.createElement('style'); style.innerHTML = ` #metrics-extractor-container { position: fixed; top: 20px; right: 20px; width: 320px; background: rgba(255, 255, 255, 0.95); border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.2); padding: 15px; z-index: 10000; font-family: Arial, sans-serif; max-height: 90vh; overflow-y: auto; } #metrics-extractor-container h2 { text-align: center; margin-top: 0; font-size: 18px; } #metrics-extractor-container table { width: 100%; border-collapse: collapse; margin-bottom: 15px; } #metrics-extractor-container th, #metrics-extractor-container td { border: 1px solid #ddd; padding: 8px; text-align: center; font-size: 14px; } #metrics-extractor-container th { background-color: #f2f2f2; } #metrics-extractor-container button { width: 100%; padding: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; margin-bottom: 10px; } #metrics-extractor-container button:hover { background-color: #45a049; } #metrics-extractor-details { display: none; max-height: 300px; overflow-y: auto; } `; document.head.appendChild(style); // 创建容器 const container = document.createElement('div'); container.id = 'metrics-extractor-container'; // 创建标题 const title = document.createElement('h2'); title.textContent = '指标总数'; container.appendChild(title); // 创建总数表格 const totalsTable = document.createElement('table'); const totalsThead = document.createElement('thead'); const totalsHeaderRow = document.createElement('tr'); const metricHeader = document.createElement('th'); metricHeader.textContent = '指标'; const totalHeader = document.createElement('th'); totalHeader.textContent = '总数'; totalsHeaderRow.appendChild(metricHeader); totalsHeaderRow.appendChild(totalHeader); totalsThead.appendChild(totalsHeaderRow); totalsTable.appendChild(totalsThead); const totalsTbody = document.createElement('tbody'); for (const [metric, total] of Object.entries(totals)) { const row = document.createElement('tr'); const metricCell = document.createElement('td'); metricCell.textContent = metric; const totalCell = document.createElement('td'); totalCell.textContent = total; row.appendChild(metricCell); row.appendChild(totalCell); totalsTbody.appendChild(row); } totalsTable.appendChild(totalsTbody); container.appendChild(totalsTable); // 创建按钮 const toggleButton = document.createElement('button'); toggleButton.id = 'metrics-extractor-toggle'; toggleButton.textContent = '显示详细数据'; container.appendChild(toggleButton); // 创建详细数据部分 const detailsSection = document.createElement('div'); detailsSection.id = 'metrics-extractor-details'; const detailsTitle = document.createElement('h2'); detailsTitle.textContent = '详细数据'; detailsSection.appendChild(detailsTitle); const detailsTable = document.createElement('table'); const detailsThead = document.createElement('thead'); const detailsHeaderRow = document.createElement('tr'); const projectHeader = document.createElement('th'); projectHeader.textContent = '项目'; detailsHeaderRow.appendChild(projectHeader); metrics.forEach(metric => { const th = document.createElement('th'); th.textContent = metric; detailsHeaderRow.appendChild(th); }); detailsThead.appendChild(detailsHeaderRow); detailsTable.appendChild(detailsThead); const detailsTbody = document.createElement('tbody'); categorizedData.forEach((item, index) => { const row = document.createElement('tr'); const projectCell = document.createElement('td'); projectCell.textContent = index + 1; row.appendChild(projectCell); metrics.forEach(metric => { const cell = document.createElement('td'); cell.textContent = item[metric] !== null ? item[metric] : '-'; row.appendChild(cell); }); detailsTbody.appendChild(row); }); detailsTable.appendChild(detailsTbody); detailsSection.appendChild(detailsTable); container.appendChild(detailsSection); document.body.appendChild(container); // 添加按钮点击事件 toggleButton.addEventListener('click', () => { if (detailsSection.style.display === 'none' || detailsSection.style.display === '') { detailsSection.style.display = 'block'; toggleButton.textContent = '隐藏详细数据'; } else { detailsSection.style.display = 'none'; toggleButton.textContent = '显示详细数据'; } }); console.log('指标面板已创建。'); } /** * 更新指标展示面板的数据 */ function updateMetricsPanel() { console.log('更新指标面板数据...'); const totalsTableBody = document.querySelector('#metrics-extractor-container table tbody'); const detailsTableBody = document.querySelector('#metrics-extractor-details table tbody'); // 更新总数表格 totalsTableBody.innerHTML = ''; for (const [metric, total] of Object.entries(totals)) { const row = document.createElement('tr'); const metricCell = document.createElement('td'); metricCell.textContent = metric; const totalCell = document.createElement('td'); totalCell.textContent = total; row.appendChild(metricCell); row.appendChild(totalCell); totalsTableBody.appendChild(row); } // 更新详细数据表格 detailsTableBody.innerHTML = ''; categorizedData.forEach((item, index) => { const row = document.createElement('tr'); const projectCell = document.createElement('td'); projectCell.textContent = index + 1; row.appendChild(projectCell); metrics.forEach(metric => { const cell = document.createElement('td'); cell.textContent = item[metric] !== null ? item[metric] : '-'; row.appendChild(cell); }); detailsTableBody.appendChild(row); }); console.log('指标面板数据已更新。'); } /** * 等待指定的条件满足 * @param {Function} conditionFunction - 返回布尔值的函数,表示条件是否满足 * @param {number} timeout - 最大等待时间(毫秒) * @param {number} interval - 检查间隔时间(毫秒) * @returns {Promise<boolean>} 条件是否在超时前满足 */ function waitFor(conditionFunction, timeout = 15000, interval = 500) { return new Promise((resolve) => { const startTime = Date.now(); const checkCondition = () => { if (conditionFunction()) { resolve(true); } else if (Date.now() - startTime >= timeout) { resolve(false); } else { setTimeout(checkCondition, interval); } }; checkCondition(); }); } /** * 初始化脚本 */ async function init() { console.log('Metrics Extractor 脚本初始化...'); // 使用更通用的选择器,等待至少2个div.icon元素存在且其span内有内容 const elementsLoaded = await waitFor(() => { const icons = document.querySelectorAll('div.icon, div[class*="icon"]'); if (icons.length >= 2) { return Array.from(icons).every(div => { const span = div.querySelector('span'); return span && span.textContent.trim() !== ''; }); } return false; }, 15000, 500); if (!elementsLoaded) { console.warn('等待目标元素超时,未找到足够的div.icon元素或span内无内容。'); return; } const extractionSuccess = extractMetrics(); if (!extractionSuccess) { console.warn('提取指标数据失败。'); return; } console.log('Metrics Extractor 脚本运行完毕。'); } // 运行初始化函数 init(); })();