您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在知乎回答页面,查询并显示作者的MCN机构信息。
// ==UserScript== // @name Zhihu MCN & Date Displayer // @namespace http://tampermonkey.net/ // @version 1.2 // @description 在知乎回答页面,查询并显示作者的MCN机构信息。 // @author Kiddo // @match *://www.zhihu.com/* // @match *://zhuanlan.zhihu.com/* // @icon https://static.zhihu.com/heifetz/favicon.ico // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function() { 'use strict'; console.log('[MCN & Date Displayer] Script started.'); const PROCESSED_MARKER = 'data-date-processed-v1'; // 使用新标记以防旧标记冲突 const MCN_PROCESSED_MARKER = 'data-mcn-processed-v1'; // 使用新标记以防旧标记冲突 /** * 将MCN信息添加到作者头部 * @param {HTMLElement} authorDiv - AuthorInfo-detail 所在的 div * @param {string} mcnCompany - MCN公司名称 * @param {string} urlToken - 用户urlToken */ function displayMcnInfo(authorDiv, mcnCompany, urlToken) { // MCN信息依然添加到 AuthorInfo-head const headDiv = authorDiv.closest('.AuthorInfo').querySelector('.AuthorInfo-content .AuthorInfo-head'); if (headDiv && !headDiv.hasAttribute('data-mcn-added')) { // 检查MCN是否已添加 const mcnSpan = document.createElement('span'); mcnSpan.className = 'mcn-info-span'; mcnSpan.textContent = `MCN: ${mcnCompany}`; mcnSpan.style.marginLeft = '8px'; mcnSpan.style.fontSize = '12px'; mcnSpan.style.color = '#8590a6'; mcnSpan.style.backgroundColor = '#f0f2f7'; mcnSpan.style.padding = '1px 4px'; mcnSpan.style.borderRadius = '3px'; mcnSpan.style.whiteSpace = 'nowrap'; // 防止换行 headDiv.appendChild(mcnSpan); headDiv.setAttribute('data-mcn-added', 'true'); // 标记MCN已添加 console.log(`[MCN & Date Displayer] Added MCN info for ${urlToken}: ${mcnCompany}`); } } /** * 将创建和修改日期添加到 AuthorInfo-badgeText * @param {HTMLElement} contentItem - 对应的 ContentItem AnswerItem 元素 */ function displayDateInfo(contentItem) { const dateCreatedMeta = contentItem.querySelector('meta[itemprop="dateCreated"]'); const dateModifiedMeta = contentItem.querySelector('meta[itemprop="dateModified"]'); // const badgeTextDiv = contentItem.querySelector('.AuthorInfo-head'); const badgeTextDiv = contentItem.querySelector('div[itemprop="zhihu:question"]'); if (badgeTextDiv && !badgeTextDiv.hasAttribute('data-date-added')) { // 检查日期是否已添加 let dateInfo = ''; let createdDate = ''; let modifiedDate = ''; if (dateCreatedMeta && dateCreatedMeta.getAttribute('content')) { // 格式化日期:例如 "2020-04-02" createdDate = new Date(dateCreatedMeta.getAttribute('content')).toISOString().split('T')[0]; dateInfo += `创建: ${createdDate}`; } if (dateModifiedMeta && dateModifiedMeta.getAttribute('content')) { modifiedDate = new Date(dateModifiedMeta.getAttribute('content')).toISOString().split('T')[0]; if (createdDate !== modifiedDate) { // 只有创建和修改日期不同才显示修改日期 if (dateInfo) { dateInfo += ' | '; } dateInfo += `修改: ${modifiedDate}`; } } if (dateInfo) { const dateSpan = document.createElement('span'); dateSpan.className = 'date-info-span'; // 插入到现有文本前或后,这里选择追加,可以调整 dateSpan.textContent = ` (${dateInfo})`; dateSpan.style.fontSize = '12px'; dateSpan.style.color = '#8590a6'; dateSpan.style.whiteSpace = 'nowrap'; // 防止换行 // 为了不影响原有的徽章文本显示,可以考虑加在徽章文本的后面 badgeTextDiv.appendChild(dateSpan); badgeTextDiv.setAttribute('data-date-added', 'true'); // 标记日期已添加 console.log(`[MCN & Date Displayer] Added date info for answer: ${createdDate} | ${modifiedDate}`); } } } /** * 处理页面上未处理的 ContentItem AnswerItem */ function processContentItems() { console.log('[MCN & Date Displayer] processContentItems() called.'); // 查找所有未处理Date的 ContentItem AnswerItem const contentItems = document.querySelectorAll(`.ContentItem.AnswerItem:not([${PROCESSED_MARKER}])`); if (contentItems.length > 0) { console.log(`[Date Displayer] Found ${contentItems.length} new ContentItem AnswerItem blocks to process.`); contentItems.forEach(contentItem => { // 提取日期信息并显示 displayDateInfo(contentItem); contentItem.setAttribute(PROCESSED_MARKER, 'true'); // 标记为已处理 }); } else { console.log('[Date Displayer] No new ContentItem AnswerItem blocks found.'); } // 查找所有未处理的MCN ContentItem AnswerItem const mcnContentItems = document.querySelectorAll(`.ContentItem.AnswerItem:not([${MCN_PROCESSED_MARKER}])`); if (mcnContentItems.length > 0) { console.log(`[MCN Displayer] Found ${mcnContentItems.length} new ContentItem AnswerItem blocks to process.`); mcnContentItems.forEach(contentItem => { // 提取MCN信息并显示 const authorDiv = contentItem.querySelector('.AuthorInfo'); if (authorDiv) { fetchAndDisplayMcn(authorDiv); contentItem.setAttribute(MCN_PROCESSED_MARKER, 'true'); // 标记为已处理 } else { console.warn('[MCN Displayer] Could not find .AuthorInfo in:', contentItem); } }); } else { console.log('[MCN Displayer] No new ContentItem AnswerItem blocks found.'); } } /** * 抓取并显示MCN信息 (原函数,略有调整以适应新的流程) * @param {HTMLElement} authorDiv - AuthorInfo AnswerItem-authorInfo 元素 */ function fetchAndDisplayMcn(authorDiv) { // MCN信息现在只在 displayMcnInfo 内部检查是否已添加, // 这里的 PROCESSED_MARKER 主要用于标记整个 contentItem // if (authorDiv.hasAttribute(PROCESSED_MARKER)) { // 这个标记现在移动到 contentItem 上 // return; // } // authorDiv.setAttribute(PROCESSED_MARKER, 'true'); // 这个标记现在移动到 contentItem 上 const metaUrlElement = authorDiv.querySelector('meta[itemprop="url"]'); if (!metaUrlElement) { console.warn('[MCN & Date Displayer] Could not find meta itemprop="url" in:', authorDiv); return; } let profileUrl = metaUrlElement.getAttribute('content'); if (!profileUrl) { console.warn('[MCN & Date Displayer] Profile URL is empty in:', authorDiv); return; } if (profileUrl.startsWith('//')) { profileUrl = 'https:' + profileUrl; } const urlParts = profileUrl.split('/'); const urlToken = urlParts[urlParts.length - 1].split('?')[0]; // 移除URL参数 if (!urlToken) { console.warn('[MCN & Date Displayer] Could not extract urlToken from:', profileUrl); return; } console.log(`[MCN & Date Displayer] Processing author: ${urlToken}, URL: ${profileUrl}`); GM_xmlhttpRequest({ method: "GET", url: profileUrl, onload: function(response) { if (response.status >= 200 && response.status < 300) { try { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const scriptElement = doc.querySelector('script#js-initialData[type="text/json"]'); if (scriptElement && scriptElement.textContent) { const jsonData = JSON.parse(scriptElement.textContent); const mcnCompany = jsonData?.initialState?.entities?.users?.[urlToken]?.mcnCompany; if (mcnCompany) { displayMcnInfo(authorDiv, mcnCompany, urlToken); } else { console.log(`[MCN & Date Displayer] No MCN info found for ${urlToken}.`); displayMcnInfo(authorDiv, 'NoMCN', urlToken); // 明确显示无MCN } } else { // console.warn(`[MCN & Date Displayer] Could not find js-initialData script for ${urlToken} in profile page.`); // 有些用户可能没有这个script,是正常现象 } } catch (e) { console.error(`[MCN & Date Displayer] Error parsing JSON for ${urlToken}:`, e, response.responseText.substring(0, 500)); } } else { console.error(`[MCN & Date Displayer] Error fetching profile for ${urlToken}. Status: ${response.status} ${response.statusText}`); } }, onerror: function(response) { console.error(`[MCN & Date Displayer] GM_xmlhttpRequest error for ${urlToken}:`, response); } }); } // 防抖函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 使用防抖包装 processContentItems const debouncedProcessContentItems = debounce(processContentItems, 500); // 500毫秒的延迟 // 初始加载时处理一次 setTimeout(processContentItems, 1000); // 确保页面元素基本加载完毕 console.log('[MyScript] Setting up MutationObserver...'); const observer = new MutationObserver((mutationsList) => { // 我们不需要详细分析mutation的内容。 // 只要DOM有变化,就可能出现了我们需要处理的新内容。 // processContentItems 函数自身的逻辑足够智能去处理。 // 我们只检查是否有节点增删,以避免因属性变化等无关操作频繁触发。 for (const mutation of mutationsList) { if (mutation.type === 'childList' && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) { // DOM结构发生了变化,调用我们的防抖处理函数 debouncedProcessContentItems(); // 只要有一个相关的mutation,就可以触发检查,无需遍历所有 return; } } }); const config = { childList: true, subtree: true }; observer.observe(document.body, config); console.log('[MCN & Date Displayer] MutationObserver is now observing document.body.'); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址