您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
auto get salt data based CreatorRangePicker-PopoverContent
// ==UserScript== // @name zhihu_salt_analysis // @namespace http://tampermonkey.net/ // @version 2025-09-28_6 // @description auto get salt data based CreatorRangePicker-PopoverContent // @author Archimon@zhihu // @match https://www.zhihu.com/creator/knowledge-income // @icon https://picx.zhimg.com/v2-1de07498cdef102d69ed02e275c51ba9_xll.jpg?source=32738c0c&needBackground=1 // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js // @grant none // @license MIT // ==/UserScript== function formatDate() { const date = new Date(); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const yesterday = String(date.getDate()-1); // 去掉补零 return `${yesterday}`; } // 等待元素加载的辅助函数 function waitForElement(selector, timeout = 5000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver(() => { const element = document.querySelector(selector); if (element) { observer.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error(`Element ${selector} not found within ${timeout}ms`)); }, timeout); }); } // 等待指定时间的辅助函数 function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 检测元素消失的辅助函数 function waitForElementDisappear(selector, timeout = 10000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (!element) { resolve(); return; } const observer = new MutationObserver(() => { const currentElement = document.querySelector(selector); if (!currentElement) { observer.disconnect(); resolve(); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error(`Element ${selector} still exists after ${timeout}ms`)); }, timeout); }); } // 监控日期选择器变化的函数(支持多次选择) function monitorDatePickerChanges(callback) { console.log('开始监控日期选择器变化...'); const datePickerSelector = 'div[class*="CreatorRangePicker-PopoverContent"]'; let isMonitoring = true; const observer = new MutationObserver(() => { if (!isMonitoring) return; const datePicker = document.querySelector(datePickerSelector); if (datePicker) { console.log('日期选择器出现,开始监控消失...'); // 监控日期选择器消失 const disappearObserver = new MutationObserver(() => { const currentPicker = document.querySelector(datePickerSelector); if (!currentPicker) { disappearObserver.disconnect(); console.log('日期选择器消失,执行回调...'); // 等待页面稳定后执行回调 setTimeout(async () => { try { await waitForPageLoad(); if (callback && typeof callback === 'function') { await callback(); } } catch (error) { console.warn('回调执行失败:', error.message); } }, 500); } }); disappearObserver.observe(document.body, { childList: true, subtree: true }); // 设置超时,避免无限监控 setTimeout(() => { disappearObserver.disconnect(); }, 15000); } }); observer.observe(document.body, { childList: true, subtree: true }); // 返回停止监控的函数 return () => { isMonitoring = false; observer.disconnect(); console.log('停止监控日期选择器变化'); }; } // 检测页面是否加载完成的函数 function waitForPageLoad(timeout = 5000) { return new Promise((resolve, reject) => { // 检查页面是否已经加载完成 if (document.readyState === 'complete') { resolve(); return; } // 监听页面加载完成事件 window.addEventListener('load', () => { resolve(); }); // 设置超时 setTimeout(() => { reject(new Error('Page load timeout')); }, timeout); }); } async function set_date(date, callback = null){ let next_button = null; const buttons = document.querySelectorAll(` input[type="button"], button[type="button"] `); for (const button of buttons) { if (button.className.includes("CreatorRangePicker-Button")){ button.click(); await wait(100); // 等待日期选择器弹出 const CreatorRangePicker = await waitForElement('div[class*="CreatorRangePicker-PopoverContent"]'); // 检查今天是否是1号 const today = new Date(); const isFirstDayOfMonth = today.getDate() === 1; let days_all; let targetDate = date; if (isFirstDayOfMonth) { // 如果是1号,使用左边的日期选择器部分,并选择上个月的最后一天 days_all = CreatorRangePicker.childNodes[0].childNodes[1].childNodes[0].childNodes[1].childNodes; // 计算上个月的最后一天 const lastMonth = new Date(today.getFullYear(), today.getMonth(), 0); targetDate = String(lastMonth.getDate()); // 去掉补零 console.log(`今天是1号,自动选择上个月最后一天:${today} \n${lastMonth} ${targetDate}`); } else { // 如果不是1号,使用右边的日期选择器部分,选择昨天 days_all = CreatorRangePicker.childNodes[1].childNodes[1].childNodes[0].childNodes[1].childNodes; console.log(`今天不是1号,选择昨天: ${targetDate}`); } console.log(CreatorRangePicker.childNodes[0].childNodes[1].tbody); let dateFound = false; for (const row of days_all) { const days_row = row.childNodes; for (const day of days_row) { if (day.textContent.trim() === targetDate){ // 双击选择日期 day.click(); await wait(120); day.click(); dateFound = true; // 日期选择后,等待选择器消失 console.log('日期已选择,等待日期选择器消失...'); try { await waitForElementDisappear('div[class*="CreatorRangePicker-PopoverContent"]'); console.log('日期选择器已消失,等待页面加载...'); // 等待页面加载完成 await waitForPageLoad(); console.log('页面加载完成'); // 如果有回调函数,执行回调 if (callback && typeof callback === 'function') { console.log('执行回调函数...'); await callback(); } } catch (error) { console.warn('日期选择器消失检测超时:', error.message); } break; // 找到日期后跳出循环 } } if (dateFound) break; } if (!dateFound) { console.warn(`未找到日期: ${targetDate},请手动选择`); } } else if (button.className.includes("CreatorPagination-nextButton")){ next_button = button; } } const benqishouyi = document.querySelectorAll('th[class*=CreatorTable-tableHead--hasSorter]'); for (const th of benqishouyi) { await wait(240); th.click(); } return next_button; } function parseRow(row) { const cells = row.querySelectorAll('td, th'); return Array.from(cells) .map(cell => { const text = cell.textContent.trim(); // 获取cell元素中的链接a href let linkHref = null; const linkElement = cell.querySelector('a'); if (linkElement && linkElement.href) { linkHref = linkElement.href; } return { text: text === '' ? null : text, linkHref: linkHref, rowspan: cell.rowSpan || 1, colspan: cell.colSpan || 1 }; }) .filter(cell => cell.text !== null && cell.text !== ''); // 过滤掉text为null或空字符串的单元格 } function prepareChartData(rows) { const fullData = []; rows.forEach((row, index) => { // 从链接中提取回答ID let ans_url = null; if (row[0].linkHref) { const match = row[0].linkHref.match(/answer\/([^\/\?]+)/); if (match) { ans_url = match[1]; } } fullData.push({ ans_names:row[0].text, ans_types:row[1].text, ans_times:row[2].text, ans_local_reads:parseInt(row[3].text.replace(/,/g, '')) || 0, ans_local_salts:parseInt(row[4].text.replace(/,/g, '')) || 0, ans_all_reads:parseInt(row[5].text.replace(/,/g, '')) || 0, ans_all_salts:parseInt(row[6].text.replace(/,/g, '')) || 0, ans_url:ans_url }); }); return fullData; } // 解析回答时间字符串为Date对象 function parseAnswerTime(timeStr) { if (!timeStr) return null; // 处理各种时间格式 if (timeStr.includes('今天')) { return new Date(); } else if (timeStr.includes('昨天')) { const date = new Date(); date.setDate(date.getDate() - 1); return date; } else if (timeStr.includes('前天')) { const date = new Date(); date.setDate(date.getDate() - 2); return date; } else if (timeStr.includes('分钟前') || timeStr.includes('小时前')) { return new Date(); // 近似处理为今天 } else { // 尝试解析标准日期格式 const date = new Date(timeStr); return isNaN(date.getTime()) ? null : date; } } // 按时间分类准备饼图数据 function prepareTimeCategoryPieChartData(fullData) { const now = new Date(); const oneDayMs = 24 * 60 * 60 * 1000; const twoDayMs = 2 * oneDayMs; const sevenDaysMs = 7 * oneDayMs; const oneMonthMs = 30 * oneDayMs; // 近似处理 const categories = { '1天以内': { salt: 0, count: 0, reads: 0 }, '1天-7天': { salt: 0, count: 0, reads: 0 }, '7天-1个月': { salt: 0, count: 0, reads: 0 }, '1个月以上': { salt: 0, count: 0, reads: 0 } }; fullData.forEach(item => { const answerTime = parseAnswerTime(item.ans_times); if (!answerTime) return; const timeDiff = now - answerTime; // console.log(`回答时间: ${item.ans_times}, 解析后: ${answerTime}, 时间差: ${timeDiff}ms`); let category = ''; if (timeDiff <= twoDayMs) { category = '1天以内'; } else if (timeDiff <= sevenDaysMs) { category = '1天-7天'; } else if (timeDiff <= oneMonthMs) { category = '7天-1个月'; } else { category = '1个月以上'; } categories[category].salt += item.ans_local_salts; categories[category].count += 1; categories[category].reads += item.ans_local_reads; }); const totalSalt = Object.values(categories).reduce((sum, cat) => sum + cat.salt, 0); const totalCount = Object.values(categories).reduce((sum, cat) => sum + cat.count, 0); const pieData = Object.entries(categories) .filter(([_, cat]) => cat.salt > 0) // 只包含有盐粒的类别 .map(([name, cat]) => ({ name: `${name} (${cat.count}个回答)`, value: cat.salt, percentage: totalSalt > 0 ? ((cat.salt / totalSalt) * 100).toFixed(2) : '0.00', salt_read_ratio: cat.reads > 0 ? (cat.salt / cat.reads).toFixed(2) : '0.00', answer_count: cat.count, read_count: cat.reads })); // 添加总统计数据 pieData.total_salt = totalSalt; pieData.total_count = totalCount; pieData.total_read = Object.values(categories).reduce((sum, cat) => sum + cat.reads, 0); pieData.total_salt_read_ratio = pieData.total_read > 0 ? (totalSalt / pieData.total_read).toFixed(2) : '0.00'; return pieData; } // 创建饼图数据 function preparePieChartData(fullData) { const salt_all_yesterday = fullData.reduce((sum, item) => sum + item.ans_local_salts, 0); const read_all_yesterday = fullData.reduce((sum, item) => sum + item.ans_local_reads, 0); // 筛选盐粒占比超过1%的元素 const significantData = fullData.filter(item => { const percentage = (item.ans_local_salts / salt_all_yesterday) * 100; return percentage >= 1; }); // 取盐粒占比超过1%的元素,或前7个,取最大值 const maxCount = Math.max(significantData.length, 7); const topData = fullData.slice(0, maxCount); const otherData = fullData.slice(maxCount); // 计算其他数据的盐粒和阅读数 const otherSalt = otherData.reduce((sum, item) => sum + item.ans_local_salts, 0); const otherRead = otherData.reduce((sum, item) => sum + item.ans_local_reads, 0); const otherCount = otherData.length; const pieData = topData.map((item, index) => ({ name: item.ans_names.length > 15 ? item.ans_names.substring(0, 15) + '...' : item.ans_names, value: item.ans_local_salts, percentage: ((item.ans_local_salts / salt_all_yesterday) * 100).toFixed(2), salt_read_ratio: (item.ans_local_salts / item.ans_local_reads).toFixed(2), read_count: item.ans_local_reads })); // 如果有其他数据,添加"其他"项 if (otherSalt > 0) { pieData.push({ name: `其他 (${otherCount}个回答)`, value: otherSalt, percentage: ((otherSalt / salt_all_yesterday) * 100).toFixed(2), salt_read_ratio: (otherSalt / otherRead).toFixed(2), answer_count: otherCount, read_count: otherRead }); } // 添加总阅读量信息 pieData.total_salt = salt_all_yesterday; pieData.total_read = read_all_yesterday; pieData.total_salt_read_ratio = (salt_all_yesterday / read_all_yesterday).toFixed(2); return pieData; } // 获取日期选择器的时间信息 function getDateRangeText() { // 查找日期选择器按钮,使用类名选择器,忽略动态生成的类名部分 const datePickerButton = document.querySelector('button[class*="CreatorRangePicker-Button"]'); if (datePickerButton) { // 获取按钮的文本内容,去除SVG图标等非文本内容 const textContent = datePickerButton.textContent.trim(); // 提取日期范围信息 const dateMatch = textContent.match(/(\d{4}\/\d{2}\/\d{2})[^]*?(\d{4}\/\d{2}\/\d{2})/); if (dateMatch) { return `${dateMatch[1]} - ${dateMatch[2]}`; } } return '未知日期'; } // 创建时间分类饼图 function createTimeCategoryPieChart(pieData, containerId = 'time-category-pie-chart') { // 创建容器 const chartContainer = document.createElement('div'); chartContainer.id = containerId; chartContainer.style.cssText = ` width: 1016px; height: 400px; margin: 20px auto; border: 1px solid #e8e8e8; border-radius: 8px; padding: 20px; background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.1); `; // 初始化图表 const chart = echarts.init(chartContainer); // 获取日期范围 const dateRange = getDateRangeText(); // 使用从prepareTimeCategoryPieChartData传递过来的统计数据 const totalSalt = pieData.total_salt || pieData.reduce((sum, item) => sum + item.value, 0); const totalCount = pieData.total_count || pieData.reduce((sum, item) => sum + item.answer_count, 0); const totalRead = pieData.total_read || pieData.reduce((sum, item) => sum + (item.read_count || 0), 0); const totalSaltReadRatio = pieData.total_salt_read_ratio || (totalRead > 0 ? (totalSalt / totalRead).toFixed(2) : '0.00'); // 配置项 const option = { title: { text: `盐粒时间分布图\n${dateRange}\n共${totalCount}个回答 | 总盐粒: ${totalSalt} | 总盐粒阅读比: ${totalSaltReadRatio}`, left: 'center', textStyle: { fontSize: 16, fontWeight: 'bold' } }, tooltip: { trigger: 'item', formatter: function(params) { return `${params.name}<br/>盐粒: ${params.value}<br/>占比: ${params.data.percentage}%<br/>盐粒阅读比: ${params.data.salt_read_ratio}<br/>回答数: ${params.data.answer_count}`; } }, legend: { orient: 'vertical', right: 10, top: 'center', type: 'scroll', pageTextStyle: { color: '#666' } }, series: [ { name: '时间分布', type: 'pie', radius: ['40%', '70%'], center: ['40%', '50%'], avoidLabelOverlap: false, itemStyle: { borderColor: '#fff', borderWidth: 2 }, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold', formatter: '{b}\n{c}盐粒\n({d}%)' } }, labelLine: { show: false }, data: pieData.map(item => ({ name: item.name, value: item.value, percentage: item.percentage, salt_read_ratio: item.salt_read_ratio, answer_count: item.answer_count })) } ], color: [ '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57', '#ff9ff3', '#54a0ff', '#5f27cd' ] }; // 设置配置项并渲染 chart.setOption(option); // 响应窗口大小变化 window.addEventListener('resize', function() { chart.resize(); }); return chartContainer; } // 创建ECharts饼图 function createPieChart(pieData, fullDataLength, containerId = 'salt-pie-chart') { // 创建容器 const chartContainer = document.createElement('div'); chartContainer.id = containerId; chartContainer.style.cssText = ` width: 1016px; height: 400px; margin: 20px auto; border: 1px solid #e8e8e8; border-radius: 8px; padding: 20px; background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.1); `; // 初始化图表 const chart = echarts.init(chartContainer); // 获取日期范围 const dateRange = getDateRangeText(); // 使用从preparePieChartData传递过来的总盐粒量和总盐粒阅读比 const totalSalt = pieData.total_salt || pieData.reduce((sum, item) => sum + item.value, 0); const totalRead = pieData.total_read || pieData.reduce((sum, item) => sum + (item.read_count || 0), 0); const totalSaltReadRatio = pieData.total_salt_read_ratio || (totalRead > 0 ? (totalSalt / totalRead).toFixed(2) : '0.00'); // 配置项 const option = { title: { text: `盐粒来源分布图\n${dateRange}\n共${fullDataLength}个回答 | 总盐粒: ${totalSalt} | 总盐粒阅读比: ${totalSaltReadRatio}`, left: 'center', textStyle: { fontSize: 16, fontWeight: 'bold' } }, tooltip: { trigger: 'item', formatter: function(params) { return `${params.name}<br/>盐粒: ${params.value}<br/>占比: ${params.data.percentage}%<br/>盐粒阅读比: ${params.data.salt_read_ratio}`; } }, legend: { orient: 'vertical', right: 10, top: 'center', type: 'scroll', pageTextStyle: { color: '#666' } }, series: [ { name: '盐粒分布', type: 'pie', radius: ['40%', '70%'], center: ['40%', '50%'], avoidLabelOverlap: false, itemStyle: { borderColor: '#fff', borderWidth: 2 }, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold', formatter: '{b}\n{c}盐粒\n({d}%)' } }, labelLine: { show: false }, data: pieData.map(item => ({ name: item.name, value: item.value, percentage: item.percentage, salt_read_ratio: item.salt_read_ratio })) } ], color: [ '#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc' ] }; // 设置配置项并渲染 chart.setOption(option); // 响应窗口大小变化 window.addEventListener('resize', function() { chart.resize(); }); return chartContainer; } function createDataTable(fullData) { let tableHtml = `<div style="position:relative;left:20px;top:30px"> <table class="CreatorTable-table ToolsRecommendList-Table" cellspacing="0" cellpadding="0"> <thead> <tr class="CreatorTable-tableRow"> <th class="CreatorTable-tableHead css-0" width="140" data-tooltip-classname="CreatorTable-Tooltip" data-tooltip-position="bottom" style="text-align: center;">内容</th> <th class="CreatorTable-tableHead css-0" width="140" data-tooltip-classname="CreatorTable-Tooltip" data-tooltip-position="bottom" style="text-align: center;">本期盐粒</th> <th class="CreatorTable-tableHead css-0" width="140" data-tooltip-classname="CreatorTable-Tooltip" data-tooltip-position="bottom" style="text-align: center;">收益占比</th> <th class="CreatorTable-tableHead css-0" width="140" data-tooltip-classname="CreatorTable-Tooltip" data-tooltip-position="bottom" style="text-align: center;">盐粒阅读比</th> </tr> </thead> <tbody> `; const salt_all_range = fullData.reduce((sum, item) => sum + item.ans_local_salts, 0); const read_all_yesterday = fullData.reduce((sum, item) => sum + item.ans_local_reads, 0); tableHtml += `<tr> <td class="CreatorTable-tableData css-0" style="text-align: center;">${fullData.length}个回答</td> <td class="CreatorTable-tableData css-0" style="text-align: center;">${salt_all_range}</td> <td class="CreatorTable-tableData css-0" style="text-align: center;">100%</td> <td class="CreatorTable-tableData css-0" style="text-align: center;">${(salt_all_range/read_all_yesterday).toFixed(2)}</td> </tr> `; fullData.forEach((item,index) => { const percentage = ((item.ans_local_salts / salt_all_range) * 100).toFixed(2); tableHtml += `<tr> <td class="CreatorTable-tableData css-0" style="text-align: center;"><div class="css-13zgqlo"><a href="https://www.zhihu.com/answer/${item.ans_url}" target="_blank" rel="noopener noreferrer" style="font-size: 14px; color: rgb(25, 27, 31); font-weight: 500; display: -webkit-box; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 3; -webkit-box-orient: vertical; white-space: pre-wrap;">${item.ans_names}</a></div></td> <td class="CreatorTable-tableData css-0" style="text-align: center;">${item.ans_local_salts}</td> <td class="CreatorTable-tableData css-0" style="text-align: center;">${percentage}%</td> <td class="CreatorTable-tableData css-0" style="text-align: center;">${(item.ans_local_salts/item.ans_local_reads).toFixed(2)}</td> </tr> `; }); tableHtml += ` </tbody> </table></div> `; return tableHtml; } async function get_data_draw(next_page_button){ const table_data = { headers: [], rows: [], }; const page_num = document.querySelector('div[class*="CreatorPagination-pageNumber"]'); const end_page = parseInt(page_num.textContent.split('/')[1].trim()); for (let i = 1; i <= end_page; i++) { // 等待页面稳定 await wait(500); let table = document.querySelector('table[class*=ToolsRecommendList-Table]'); if (!table) { console.warn(`第 ${i} 页表格未找到,等待重试...`); await wait(1000); table = document.querySelector('table[class*=ToolsRecommendList-Table]'); } if (table) { let thead = table.childNodes[0]; table_data.headers = parseRow(thead); let data_rows = table.childNodes[1].querySelectorAll('tr'); let parsed_rows = Array.from(data_rows).map(row => parseRow(row)); table_data.rows = Array.from(table_data.rows).concat(parsed_rows).filter(row => row.length !== 0); // console.log(table_data.rows); } if (i < end_page && next_page_button) { next_page_button.click(); // 等待页面加载完成 await waitForElement('table[class*=ToolsRecommendList-Table]'); } } await wait(500); const all_data = prepareChartData(Array.from(table_data.rows)); const salt_all_range = all_data.reduce((sum, item) => sum + item.ans_local_salts, 0); // 输出控制台信息 all_data.sort((a,b) => b.ans_local_salts- a.ans_local_salts).forEach(item => { const percentage = ((item.ans_local_salts / salt_all_range) * 100).toFixed(2); console.log(`${String(item.ans_local_salts).padStart(8,' ')} ${String(percentage).padStart(6,' ')}% ${item.ans_names} `); }); // 创建可视化界面 await createVisualization(all_data); } // 创建可视化界面(饼图 + 表格) async function createVisualization(all_data) { // 准备饼图数据 const pieData = preparePieChartData(all_data); const timeCategoryData = prepareTimeCategoryPieChartData(all_data); // 创建主容器 const container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 10000; max-height: 90vh; overflow-y: auto; min-width: 800px; `; // 创建关闭按钮 const closeButton = document.createElement('button'); closeButton.textContent = '×'; closeButton.style.cssText = ` position: absolute; top: 10px; right: 10px; background: none; border: none; font-size: 24px; cursor: pointer; color: #666; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; `; closeButton.onclick = () => container.remove(); // 创建标题 const title = document.createElement('h2'); title.textContent = '知乎盐粒收益分析'; title.style.cssText = ` text-align: center; margin-bottom: 20px; color: #1890ff; font-size: 18px; `; // 创建盐粒来源饼图 const pieChart = createPieChart(pieData, all_data.length); // 创建时间分类饼图 const timeCategoryPieChart = createTimeCategoryPieChart(timeCategoryData); // 创建表格 const tableHtml = createDataTable(all_data); const tableContainer = document.createElement('div'); tableContainer.innerHTML = tableHtml; // 组装所有元素 container.appendChild(closeButton); container.appendChild(title); container.appendChild(pieChart); container.appendChild(timeCategoryPieChart); container.appendChild(tableContainer); // 添加到页面 document.body.appendChild(container); // 添加ESC键关闭功能 const handleKeydown = (e) => { if (e.key === 'Escape') { container.remove(); document.removeEventListener('keydown', handleKeydown); } }; document.addEventListener('keydown', handleKeydown); // 点击背景关闭 const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; `; overlay.onclick = () => { container.remove(); overlay.remove(); document.removeEventListener('keydown', handleKeydown); }; document.body.appendChild(overlay); } (async function() { 'use strict'; console.log(`🚀 [email protected] say hello!`); // 等待页面完全加载 if (document.readyState === 'loading') { await new Promise(resolve => { document.addEventListener('DOMContentLoaded', resolve); }); } // 添加样式 // 获取昨天日期 const yesterday_str = formatDate(); // 点击进入『内容收益明细』 const clickableDivs = document.querySelectorAll('div[class*="clickable"]'); for (const div of clickableDivs) { const textContent = div.textContent.trim(); if (textContent.includes('内容收益明细')) { div.click(); break; // 只点击第一个匹配的元素 } } // 等待页面跳转完成 await wait(500); // 定义回调函数,在日期选择完成后执行 const dataDrawCallback = async () => { console.log('回调函数执行:开始获取数据并绘制图表...'); // 重新获取下一页按钮(因为页面可能已刷新) const next_button = document.querySelector('button[class*="CreatorPagination-nextButton"]'); if (next_button) { await wait(720); await get_data_draw(next_button); } else { console.error('未找到下一页按钮,尝试直接获取数据...'); await get_data_draw(null); } }; // 初始设置日期 await set_date(yesterday_str, dataDrawCallback); // 启动日期选择器变化监控(支持第二次及后续的时间选择) console.log('启动日期选择器变化监控...'); const stopMonitoring = monitorDatePickerChanges(dataDrawCallback); // 页面卸载时停止监控 window.addEventListener('beforeunload', () => { stopMonitoring(); }); // 添加停止监控的全局函数(用于调试) window.stopDatePickerMonitoring = stopMonitoring; })();