// ==UserScript== // @name 微博净化器·内容屏蔽助手 // @namespace http://tampermonkey.net/ // @version 7.1 // @description 信息流屏蔽+正文屏蔽+导航屏蔽+侧边栏屏蔽+评论用户屏蔽(完整线程)+自定义分组屏蔽+广告图片屏蔽+搜索页横幅广告屏蔽 // @author MRBANK // @match https://weibo.com/* // @match https://*.weibo.com/* // @icon https://weibo.com/favicon.ico // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function() { 'use strict'; // ================== 配置管理 ================== const CONFIG = { // 功能开关 blockAds: GM_getValue('blockAds', true), blockImages: GM_getValue('blockImages', true), blockTopNav: GM_getValue('blockTopNav', true), blockAdImages: GM_getValue('blockAdImages', true), blockSearchBannerAds: GM_getValue('blockSearchBannerAds', false), // 搜索页横幅图片广告独立开关,默认关闭 debugMode: GM_getValue('debugMode', false), // 信息流标签关键词(可增删) blockKeywords: GM_getValue('blockKeywords', [ { text: '荐读', enabled: true }, { text: '推荐', enabled: true }, { text: '公益', enabled: true }, { text: '广告', enabled: true } ]), // 正文关键词(可增删,默认空数组) contentKeywords: GM_getValue('contentKeywords', []), // 顶部导航屏蔽关键词(固定预设,不可增删) topNavKeywords: GM_getValue('topNavKeywords', [ { text: '首页', enabled: false }, { text: '消息', enabled: false }, { text: '推荐', enabled: true }, { text: '视频', enabled: true }, { text: '游戏', enabled: true }, { text: '无障碍', enabled: true } ]), // 侧边栏模块固定预设(用户不可增删,仅可开关) sidebarModules: GM_getValue('sidebarModules', [ { text: '你可能感兴趣的人', enabled: false }, { text: '创作者中心', enabled: false }, { text: '微博热搜', enabled: false }, { text: '自定义分组', enabled: false }, { text: '底部信息', enabled: false } ]), // 评论用户屏蔽(可增删) blockCommentUsers: GM_getValue('blockCommentUsers', []), // 广告图片域名屏蔽(可增删,基于图片src域名匹配整个广告容器) adImageDomains: GM_getValue('adImageDomains', [ { text: 'kadmimage.biz.weibo.com', enabled: true } ]), // 性能配置 observerDelay: 50, scrollThrottle: 500, batchSize: 10, // 选择器配置(已适配新版微博 + 搜索页,移除 wrap-continuous 避免连带隐藏) selectors: { feedItem: 'article.Feed_wrap_3v9LH.Feed_normal_12A98, article:has(.wbpro-feed-content), article[class*="woo-panel"], div.card-wrap[action-type="feed_list_item"]', tagContainer: '.wbpro-tag div, .wbpro-tag, p.from > span', feedContent: '[class*="_wbtext_"], [class*="Feed_content"] [class*="txt"], [class*="content"] [class*="txt"], p.txt', sidebarModule: '.wbpro-side', sidebarTitle: '.wbpro-side-tit .f16.fm.cla' } }; // ================== 临时配置(用于弹窗编辑)================== let TEMP_CONFIG = JSON.parse(JSON.stringify(CONFIG)); // ================== 工具函数 ================== function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } function log(message, type = 'info', data = null) { if (!CONFIG.debugMode && type !== 'error') return; const prefix = '【微博屏蔽 v6.9】'; const timestamp = new Date().toLocaleTimeString(); switch(type) { case 'error': console.error(`${prefix}[${timestamp}]`, message, data || ''); break; case 'warn': console.warn(`${prefix}[${timestamp}]`, message, data || ''); break; case 'success': console.log(`%c${prefix}[${timestamp}] ${message}`, 'color: green', data || ''); break; default: console.log(`${prefix}[${timestamp}]`, message, data || ''); } } const performanceMonitor = { stats: { blockedItems: 0, blockedImages: 0, blockedSidebars: 0, blockedTopNav: 0, blockedComments: 0, blockedAdImages: 0, executionTime: 0 }, start() { return performance.now(); }, end(startTime, operation) { const duration = performance.now() - startTime; this.stats.executionTime += duration; if (CONFIG.debugMode) log(`${operation} 耗时: ${duration.toFixed(2)}ms`, 'info'); }, report() { log('性能统计', 'success', this.stats); } }; // ================== 核心屏蔽功能 ================== function injectStyles() { let adImageCSS = ''; if (CONFIG.blockAdImages) { const domains = CONFIG.adImageDomains.filter(d => d.enabled).map(d => d.text); domains.forEach(domain => { adImageCSS += `img[src*="${domain}"] { display: none !important; visibility: hidden !important; opacity: 0 !important; }\n`; }); } const styles = ` ${CONFIG.blockImages ? ` img[src*="shuakong"] { display: none !important; visibility: hidden !important; opacity: 0 !important; width: 0 !important; height: 0 !important; } ` : ''} ${adImageCSS} .weibo-ad-blocked, .weibo-topnav-blocked, .weibo-sidebar-blocked, .weibo-comment-blocked, .weibo-ad-image-blocked { display: none !important; } .weibo-content-blocked { overflow: hidden !important; } .weibo-content-blocked header, .weibo-content-blocked footer, .weibo-content-blocked [class*="_head_"], .weibo-content-blocked [class*="_header_"], .weibo-content-blocked [class*="_info_"], .weibo-content-blocked [class*="_suffixbox_"], .weibo-content-blocked [class*="_content_wrap_"], .weibo-content-blocked [class*="_followbtn_"], .weibo-content-blocked [class*="_more_"], .weibo-content-blocked .woo-avatar-main, .weibo-content-blocked .woo-avatar-hover, .weibo-content-blocked .woo-avatar-img, .weibo-content-blocked .woo-button-main, .weibo-content-blocked [class*="_left_"], .weibo-content-blocked [class*="_main_"][class*="198pe"], .weibo-content-blocked [class*="_item_"], .weibo-content-blocked [class*="_wrap_"][class*="198pe"], .weibo-content-blocked [class*="_iconWrap_"], .weibo-content-blocked .woo-like-main, .weibo-content-blocked .woo-like-count, .weibo-content-blocked .woo-font--retweet, .weibo-content-blocked .woo-font--comment { display: none !important; } .weibo-content-blocked .wbpro-feed-content, .weibo-content-blocked [class*="_placebox_"], .weibo-content-blocked .picture, .weibo-content-blocked video, .weibo-content-blocked [class*="_row_"], .weibo-content-blocked [class*="_ogText_"], .weibo-content-blocked .retweet, .weibo-content-blocked [class*="_retweet_"], .weibo-content-blocked [class*="_reText_"], .weibo-content-blocked [class*="_retweetBar_"], .weibo-content-blocked [class*="_retweetHeadInfo_"] { display: none !important; } .weibo-content-blocked [class*="_body_"] { padding-bottom: 0 !important; } .weibo-content-blocked footer { margin-top: 0 !important; } ${CONFIG.debugMode ? ` .weibo-ad-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed red !important; background: rgba(255,0,0,0.1) !important; } .weibo-topnav-blocked { display: inline-block !important; opacity: 0.3 !important; border: 2px dashed blue !important; background: rgba(0,0,255,0.1) !important; } .weibo-sidebar-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed green !important; background: rgba(0,255,0,0.1) !important; } .weibo-comment-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed purple !important; background: rgba(128,0,128,0.1) !important; } .weibo-ad-image-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed orange !important; background: rgba(255,165,0,0.1) !important; } ` : ''} `; GM_addStyle(styles); log('样式注入成功', 'success'); } // 获取微博正文文本 function getContentText(container) { try { let contentEl = container.querySelector('[class*="_wbtext_"]'); if (contentEl) return contentEl.textContent.trim(); contentEl = container.querySelector(CONFIG.selectors.feedContent); if (contentEl) return contentEl.textContent.trim(); return container.textContent.trim(); } catch (e) { log('获取正文文本失败', 'error', e); return ''; } } function markBlockedContent(container) { const article = container.closest('article') || container; article.classList.add('weibo-content-blocked'); return true; } // 信息流屏蔽(精准定位 action-type,避免误杀同级横幅广告) function processFeedItems() { if (!CONFIG.blockAds) return; const startTime = performanceMonitor.start(); const feedItems = document.querySelectorAll(`${CONFIG.selectors.feedItem}:not([data-processed="true"])`); let blockedCount = 0; const enabledContentKeywords = CONFIG.contentKeywords.filter(kw => kw.enabled); const enabledTagKeywords = CONFIG.blockKeywords.filter(kw => kw.enabled); Array.from(feedItems).slice(0, CONFIG.batchSize).forEach(container => { try { container.setAttribute('data-processed', 'true'); let shouldBlock = false; let blockReason = ''; const isSearchCard = container.matches('div.card-wrap[action-type="feed_list_item"]'); const tagElements = container.querySelectorAll(CONFIG.selectors.tagContainer); if (tagElements.length > 0) { for (let i = 0; i < tagElements.length; i++) { const tagText = tagElements[i].textContent.trim(); if (tagText) { const matchedTag = enabledTagKeywords.find(kw => tagText.includes(kw.text)); if (matchedTag) { shouldBlock = true; blockReason = `标签:${matchedTag.text}`; break; } } } } if (!shouldBlock && enabledContentKeywords.length > 0) { const contentText = getContentText(container); if (contentText) { const matchedContent = enabledContentKeywords.find(kw => contentText.includes(kw.text)); if (matchedContent) { shouldBlock = true; blockReason = `正文:${matchedContent.text}`; } } } if (shouldBlock) { if (isSearchCard) { if (!container.classList.contains('weibo-ad-blocked')) { container.classList.add('weibo-ad-blocked'); blockedCount++; performanceMonitor.stats.blockedItems++; log(`屏蔽搜索页信息流: ${blockReason}`, 'success'); } } else { if (markBlockedContent(container)) { blockedCount++; performanceMonitor.stats.blockedItems++; log(`屏蔽首页信息流: ${blockReason}`, 'success'); } } } } catch (e) { log('处理信息流出错', 'error', e); } }); if (blockedCount) log(`本次屏蔽 ${blockedCount} 条内容`, 'info'); performanceMonitor.end(startTime, '处理信息流'); if (feedItems.length > CONFIG.batchSize) setTimeout(processFeedItems, 50); } // 侧边栏模块屏蔽 function processSidebarModules() { const startTime = performanceMonitor.start(); let blockedCount = 0; try { const modules = document.querySelectorAll(`${CONFIG.selectors.sidebarModule}:not([data-sidebar-processed="true"])`); modules.forEach(module => { module.setAttribute('data-sidebar-processed', 'true'); const titleEl = module.querySelector(CONFIG.selectors.sidebarTitle); if (!titleEl) return; const titleText = titleEl.textContent.trim(); const matchedModule = CONFIG.sidebarModules.filter(m => m.enabled).find(m => titleText.includes(m.text)); if (matchedModule && !module.classList.contains('weibo-sidebar-blocked')) { module.classList.add('weibo-sidebar-blocked'); blockedCount++; performanceMonitor.stats.blockedSidebars++; log(`屏蔽侧边栏模块(标准): "${titleText}"`, 'success'); } }); const enabledModules = CONFIG.sidebarModules.filter(m => m.enabled).map(m => m.text); if (enabledModules.length > 0) { const h3Titles = document.querySelectorAll('main h3, [class*="side"] h3, [class*="Side"] h3'); h3Titles.forEach(h3 => { if (h3.hasAttribute('data-flat-processed')) return; h3.setAttribute('data-flat-processed', 'true'); const titleText = h3.textContent.trim(); const matchedModule = enabledModules.find(text => titleText.includes(text)); if (matchedModule) { const headerContainer = h3.parentElement; if (headerContainer && !headerContainer.classList.contains('weibo-sidebar-blocked')) { headerContainer.classList.add('weibo-sidebar-blocked'); blockedCount++; log(`屏蔽侧边栏模块(扁平标题): "${titleText}"`, 'success'); } let nextElement = headerContainer ? headerContainer.nextElementSibling : null; while (nextElement && nextElement.tagName === 'A') { if (!nextElement.classList.contains('weibo-sidebar-blocked')) nextElement.classList.add('weibo-sidebar-blocked'); nextElement = nextElement.nextElementSibling; } } }); } const bottomInfoKeyword = CONFIG.sidebarModules.find(m => m.text === '底部信息'); if (bottomInfoKeyword && bottomInfoKeyword.enabled) { document.querySelectorAll('.wbpro-side-copy:not([data-sidebar-processed="true"])').forEach(el => { el.setAttribute('data-sidebar-processed', 'true'); if (!el.classList.contains('weibo-sidebar-blocked')) { el.classList.add('weibo-sidebar-blocked'); blockedCount++; performanceMonitor.stats.blockedSidebars++; log('屏蔽侧边栏模块(底部信息)', 'success'); } }); } } catch (e) { log('处理侧边栏模块出错', 'error', e); } if (blockedCount) log(`本次屏蔽侧边栏模块 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理侧边栏模块'); } // 顶部导航屏蔽 function processTopNav() { if (!CONFIG.blockTopNav) return; const startTime = performanceMonitor.start(); let blockedCount = 0; try { const gameKeyword = CONFIG.topNavKeywords.find(kw => kw.text === '游戏'); if (gameKeyword && gameKeyword.enabled) { const gameLink = document.querySelector('a[title="游戏"]'); if (gameLink) { const gameBox = gameLink.closest('div[class*="_box_"]'); if (gameBox && !gameBox.classList.contains('weibo-topnav-blocked')) { gameBox.classList.add('weibo-topnav-blocked'); blockedCount++; performanceMonitor.stats.blockedTopNav++; log('屏蔽顶部导航: 游戏(含分隔线)', 'success'); } } } const accessKeyword = CONFIG.topNavKeywords.find(kw => kw.text === '无障碍'); if (accessKeyword && accessKeyword.enabled) { const accessibilityBtn = document.querySelector('button#cniil_wza'); if (accessibilityBtn) { const accessBox = accessibilityBtn.closest('div[class*="_box_"]'); if (accessBox && !accessBox.classList.contains('weibo-topnav-blocked')) { accessBox.classList.add('weibo-topnav-blocked'); blockedCount++; performanceMonitor.stats.blockedTopNav++; log('屏蔽顶部导航: 无障碍(含分隔线)', 'success'); } } } let navRoot = document.querySelector('a[href="/hot"]')?.closest('header, nav, div[class*="woo-tab"], div[class*="header"]') || document.querySelector('.woo-tab-main, [class*="woo-tab-other"], div[class*="woo-tab"]') || document.querySelector('header'); if (navRoot) { const candidates = navRoot.querySelectorAll('a[title], button[title], button:not([title]), a[aria-label], div[aria-label], a[href*="/"], #cniil_wza'); const matchedTargets = new Set(); candidates.forEach(el => { if (el.closest('.weibo-topnav-blocked')) return; let matchText = ''; if (el.hasAttribute('aria-label')) matchText = el.getAttribute('aria-label'); else if (el.hasAttribute('title') && el.getAttribute('title').trim() !== '') matchText = el.getAttribute('title'); else { const span = el.querySelector('span:not(.woo-badge-box)'); matchText = span ? span.textContent.trim() : el.textContent.trim(); } if (!matchText) return; const shouldBlock = CONFIG.topNavKeywords.filter(kw => kw.enabled).some(kw => matchText.includes(kw.text)); if (shouldBlock) { let target = el.closest('div[class*="_box_"]') || (el.tagName === 'SPAN' || el.tagName === 'SVG' || el.tagName === 'PATH' ? el.closest('a, button, div[role="button"]') || el : el); if (!matchedTargets.has(target)) { target.classList.add('weibo-topnav-blocked'); matchedTargets.add(target); blockedCount++; performanceMonitor.stats.blockedTopNav++; log(`屏蔽顶部导航: "${matchText}"`, 'success'); } } }); } } catch (error) { log('处理顶部导航按钮时出错', 'error', error); } if (blockedCount > 0) log(`本次屏蔽顶部导航按钮 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理顶部导航'); } // 评论用户屏蔽 function processComments() { const enabledUsers = CONFIG.blockCommentUsers.filter(u => u.enabled).map(u => u.text); if (enabledUsers.length === 0) return; const startTime = performanceMonitor.start(); let blockedCount = 0; try { document.querySelectorAll('.item1 .text > a[href*="/u/"]').forEach(link => { const commentUnit = link.closest('.item1'); if (!commentUnit || commentUnit.closest('.weibo-comment-blocked') || commentUnit.hasAttribute('data-comment-unit-processed')) return; const authorName = link.textContent.trim(); if (enabledUsers.includes(authorName)) { commentUnit.classList.add('weibo-comment-blocked'); commentUnit.setAttribute('data-comment-unit-processed', 'true'); let next = commentUnit.nextElementSibling; while (next && !next.classList.contains('item1')) { next.classList.add('weibo-comment-blocked'); next.setAttribute('data-comment-unit-processed', 'true'); next = next.nextElementSibling; } blockedCount++; performanceMonitor.stats.blockedComments++; log(`屏蔽评论线程: 用户 "${authorName}"`, 'success'); } else commentUnit.setAttribute('data-comment-unit-processed', 'true'); }); } catch (e) { log('处理评论出错', 'error', e); } if (blockedCount) log(`本次屏蔽评论线程 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理评论'); } // 广告图片屏蔽 function blockAdContainer(imgElement) { let adContainer = imgElement.closest('div[class*="_wrap"]') || imgElement.closest('div[class*="_bottomGap"]') || imgElement.closest('div.woo-box-flex.woo-box-alignCenter.woo-box-justifyCenter'); if (adContainer && adContainer !== document.body && adContainer !== document.documentElement) { if (!adContainer.classList.contains('weibo-ad-image-blocked')) { adContainer.classList.add('weibo-ad-image-blocked'); log('屏蔽广告容器 (图片域名匹配)', 'success'); return true; } return false; } if (!imgElement.classList.contains('weibo-ad-image-blocked')) { imgElement.classList.add('weibo-ad-image-blocked'); log('屏蔽广告图片 (无合适父级容器)', 'success'); return true; } return false; } function processAdImages() { if (!CONFIG.blockAdImages) return; const startTime = performanceMonitor.start(); let blockedCount = 0; const enabledDomains = CONFIG.adImageDomains.filter(d => d.enabled).map(d => d.text); if (enabledDomains.length === 0) { performanceMonitor.end(startTime, '处理广告图片'); return; } document.querySelectorAll('img:not([data-ad-img-processed])').forEach(img => { img.setAttribute('data-ad-img-processed', 'true'); if (img.src && enabledDomains.some(domain => img.src.includes(domain))) { if (blockAdContainer(img)) { blockedCount++; performanceMonitor.stats.blockedAdImages++; } } }); if (blockedCount) log(`本次屏蔽广告图片容器 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理广告图片'); } function setupAdImageObserver() { if (!CONFIG.blockAdImages) return; const observer = new MutationObserver(mutations => { const enabledDomains = CONFIG.adImageDomains.filter(d => d.enabled).map(d => d.text); if (enabledDomains.length === 0) return; for (const mutation of mutations) { if (mutation.type === 'attributes' && mutation.attributeName === 'src') { const target = mutation.target; if (target.tagName === 'IMG' && target.src) { target.removeAttribute('data-ad-img-processed'); if (enabledDomains.some(domain => target.src.includes(domain))) { target.setAttribute('data-ad-img-processed', 'true'); if (blockAdContainer(target)) performanceMonitor.stats.blockedAdImages++; } } } } }); if (document.body) observer.observe(document.body, { attributes: true, subtree: true, attributeFilter: ['src'] }); else window.addEventListener('DOMContentLoaded', () => observer.observe(document.body, { attributes: true, subtree: true, attributeFilter: ['src'] })); log('广告图片属性监听器启动', 'success'); } // ========== 搜索页横幅图片广告屏蔽(精准命中自身,不牵连父级) ========== function processSearchBannerAds() { if (!CONFIG.blockSearchBannerAds) return; const startTime = performanceMonitor.start(); let blockedCount = 0; try { document.querySelectorAll('div.card-pic-a:not([data-search-ad-processed])').forEach(iconContainer => { iconContainer.setAttribute('data-search-ad-processed', 'true'); const icon = iconContainer.querySelector('i[style*="simg.s.weibo.com/imgtool"]'); if (icon) { // 修复:直接隐藏 card-pic-a 容器,不向上寻找 wrap-continuous,避免连带隐藏微博 if (!iconContainer.classList.contains('weibo-ad-blocked')) { iconContainer.classList.add('weibo-ad-blocked'); blockedCount++; performanceMonitor.stats.blockedItems++; log('屏蔽搜索页横幅广告', 'success'); } } }); } catch (e) { log('处理搜索页横幅广告出错', 'error', e); } if (blockedCount) log(`本次屏蔽搜索页横幅广告 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理搜索页横幅广告'); } const processContent = debounce(() => { processFeedItems(); processSidebarModules(); processTopNav(); processComments(); processAdImages(); processSearchBannerAds(); }, CONFIG.observerDelay); // ================== 可视化配置弹窗 ================== let uiPanel = null; function createUI() { if (uiPanel) return uiPanel; uiPanel = document.createElement('div'); uiPanel.id = 'weibo-adblocker-panel'; uiPanel.innerHTML = `