您需要先安装一个扩展,例如 篡改猴、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();
- })();