您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
完整功能广告拦截器,支持内容关键词过滤与智能检测
当前为
// ==UserScript== // @name 广告终结者 // @namespace http://tampermonkey.net/ // @version 2.5 // @description 完整功能广告拦截器,支持内容关键词过滤与智能检测 // @author TMHhz // @match *://*/* // @exclude *://*.bing.com/* // @exclude *://*.iqiyi.com/* // @exclude *://*.qq.com/* // @exclude *://*.v.qq.com/* // @exclude *://*.sohu.com/* // @exclude *://*.mgtv.com/* // @exclude *://*.ifeng.com/* // @exclude *://*.pptv.com/* // @exclude *://*.sina.com.cn/* // @exclude *://*.56.com/* // @exclude *://*.cntv.cn/* // @exclude *://*.tudou.com/* // @exclude *://*.baofeng.com/* // @exclude *://*.le.com/* // @exclude *://*.pps.tv/* // @exclude *://*.www.fun.tv/* // @exclude *://*.baidu.com/* // @exclude *://*.ku6.com/* // @exclude *://*.tvsou.com/* // @exclude *://*.kankan.com/* // @exclude *://*.douyu.com/* // @exclude *://*.weibo.com/* // @exclude *://*.people.com.cn/* // @exclude *://*.cctv.com/* // @exclude *://*.gdtv.com.cn/* // @exclude *://*.ahtv.cn/* // @exclude *://*.tvb.com/* // @exclude *://*.tvmao.com/* // @exclude *://*.douban.com/* // @exclude *://*.163.com/* // @exclude *://*.bilibili.com/* // @exclude *://*.www.gov.cn/* // @exclude *://*.thepaper.cn/* // @exclude *://*.xinhuanet.com/* // @exclude *://*.china.com/* // @exclude *://*.guancha.cn/* // @exclude *://*.qidian.com/* // @exclude *://*.faloo.com/* // @exclude *://*.readnovel.com/* // @exclude *://*.hongxiu.com/* // @exclude *://*.zongheng.com/* // @exclude *://*.kanshu.com/* // @exclude *://*.17k.com/* // @exclude *://*.heiyan.com/* // @exclude *://*.duokan.com/* // @exclude *://*.jianshu.com/* // @exclude *://*.amazon.cn/* // @exclude *://*.cnblogs.com/* // @exclude *://*.cnstock.com/* // @exclude *://*.baike.com/* // @exclude *://*.guokr.com/* // @exclude *://*.360doc.com/* // @exclude *://*.qiushibaike.com/* // @exclude *://*.zol.com.cn/* // @exclude *://*.pconline.com.cn/* // @exclude *://*.pcpop.com/* // @exclude *://*.it168.com/* // @exclude *://*.gfan.com/* // @exclude *://*.feng.com/* // @exclude *://*.xiaomi.cn/* // @exclude *://*.10086.cn/* // @exclude *://*.10010.com/* // @license GPLv3 // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_notification // @grant GM_addStyle // @grant GM_getResourceText // @connect self // @run-at document-start // ==/UserScript== (function() { 'use strict'; // 核心配置对象 const CONFIG = { maxLogs: 150, adKeywords: [ 'ad', 'ads', 'advert', 'banner', 'popup', '推广', '广告', 'gg', 'advertisement', 'sponsor', '推荐', 'adv', 'guanggao', 'syad', 'bfad', '男男', '女女', '弹窗', '悬浮', '浮动', '浮窗', '葡京', 'pop', 'sticky', 'fixed', 'tip', 'tips', 'adbox', 'adsense', 'adserver', 'advertmarket', 'advertising', 'cookie-sync', '偷拍', '黑料', '横幅', '乱伦' ], protectionRules: { dynamicIdLength: 12, zIndexThreshold: 50, maxFrameDepth: 3, textAdKeywords: ['限时优惠', '立即下载', '微信', 'vx:', 'telegram', '偷拍', '黑料'] }, contentFilter: { scanDepth: 3, minLength: 2, maxKeywords: 50, timeout: 300 }, defaultSettings: { dynamicSystem: true, layoutSystem: true, frameSystem: true, mediaSystem: true, textSystem: true, thirdPartyBlock: true, contentFilter: true } }; // ======================= 工具类 ======================= class AdUtils { static safeRemove(node, module, reason) { if (!node?.parentNode || this.isWhitelisted(node)) return false; try { Logger.logRemoval({ module, element: { tag: node.tagName, id: node.id, class: node.className, html: node.outerHTML?.slice(0, 200) }, reason }); node.parentNode.removeChild(node); return true; } catch(e) { console.warn('元素移除失败:', e); return false; } } static isWhitelisted(element) { return element.closest('[data-protected]') || this.hasWhitelistContent(element); } static shouldBlockByContent(element) { if (!Config.get('contentFilter')) return false; const text = this.getCleanText(element); const blacklist = KeywordManager.getBlacklist(); const whitelist = KeywordManager.getWhitelist(); if (whitelist.some(k => text.includes(k))) return false; return blacklist.some(k => text.includes(k)); } static getCleanText(element) { const rawText = this.extractText(element).trim(); return this.normalizeText(rawText); } static extractText(element, depth = 0) { if (depth > CONFIG.contentFilter.scanDepth) return ''; return Array.from(element.childNodes).map(n => { if (n.nodeType === Node.TEXT_NODE) return n.textContent; if (n.nodeType === Node.ELEMENT_NODE) { return this.extractText(n, depth + 1); } return ''; }).join(' '); } static normalizeText(text) { return text .replace(/\s+/g, ' ') .replace(/[【】《》「」“”‘’]/g, '') .toLowerCase(); } static hasWhitelistContent(element) { const text = this.normalizeText(element.textContent); return KeywordManager.getWhitelist().some(k => text.includes(k)); } } // ======================= 核心系统 ======================= class CoreSystem { constructor() { this.initObservers(); this.initialClean(); this.setupContentScanner(); this.injectProtectionStyles(); } initObservers() { new MutationObserver(mutations => { mutations.forEach(m => { m.addedNodes.forEach(n => { if(n.nodeType === 1) this.processElement(n); }); }); }).observe(document, {childList: true, subtree: true}); } initialClean() { this.checkElements('*', el => this.processElement(el)); this.checkIframes(); this.checkThirdParty(); this.scanForContent(document.documentElement); } processElement(el) { // 动态检测系统 if(Config.get('dynamicSystem')) { this.checkDynamicId(el); this.checkAdAttributes(el); } // 布局检测系统 if(Config.get('layoutSystem')) { this.checkZIndex(el); this.checkFixedPosition(el); } // 媒体检测系统 if(Config.get('mediaSystem')) { this.checkImageAds(el); this.checkFloatingAds(el); } // 文本广告检测 if(Config.get('textSystem')) { this.checkTextAds(el); } } setupContentScanner() { if (!Config.get('contentFilter')) return; new MutationObserver(mutations => { mutations.forEach(m => { m.addedNodes.forEach(n => { if (n.nodeType === 1) this.scanForContent(n); }); }); }).observe(document, { childList: true, subtree: true }); } scanForContent(element) { if (AdUtils.shouldBlockByContent(element)) { AdUtils.safeRemove(element, 'ContentFilter', { type: '内容关键词匹配', detail: '黑名单内容触发' }); } Array.from(element.children).forEach(child => this.scanForContent(child) ); } checkDynamicId(el) { const id = el.id || ''; if(id.length > CONFIG.protectionRules.dynamicIdLength || /\d{5}/.test(id)) { AdUtils.safeRemove(el, 'DynamicSystem', { type: '动态ID检测', detail: `异常ID: ${id.slice(0, 20)}` }); } } checkAdAttributes(el) { ['id', 'class', 'src'].forEach(attr => { const val = el.getAttribute(attr) || ''; if(CONFIG.adKeywords.some(k => val.includes(k))) { AdUtils.safeRemove(el, 'DynamicSystem', { type: '广告属性检测', detail: `${attr}=${val.slice(0, 30)}` }); } }); } checkZIndex(el) { const zIndex = parseInt(getComputedStyle(el).zIndex); if(zIndex > CONFIG.protectionRules.zIndexThreshold) { AdUtils.safeRemove(el, 'LayoutSystem', { type: '高堆叠元素', detail: `z-index=${zIndex}` }); } } checkFixedPosition(el) { const style = getComputedStyle(el); if(style.position === 'fixed' && el.offsetWidth < 200) { AdUtils.safeRemove(el, 'LayoutSystem', { type: '固定定位元素', detail: `尺寸: ${el.offsetWidth}x${el.offsetHeight}` }); } } checkImageAds(el) { if(el.tagName === 'IMG' && (el.src.includes('ad') || el.src.endsWith('.gif'))) { AdUtils.safeRemove(el, 'MediaSystem', { type: '图片广告', detail: `图片源: ${el.src.slice(0, 50)}` }); } } checkFloatingAds(el) { const rect = el.getBoundingClientRect(); const style = getComputedStyle(el); if(['fixed', 'sticky'].includes(style.position) && (rect.top < 10 || rect.bottom > window.innerHeight - 10)) { AdUtils.safeRemove(el, 'MediaSystem', { type: '浮动广告', detail: `位置: ${rect.top}px` }); } } checkTextAds(el) { const text = el.textContent?.toLowerCase() || ''; if (CONFIG.protectionRules.textAdKeywords.some(k => text.includes(k))) { AdUtils.safeRemove(el, 'TextSystem', { type: '文本广告', detail: `关键词: ${text.slice(0, 50)}` }); } } checkIframes() { if(!Config.get('frameSystem')) return; document.querySelectorAll('iframe').forEach(iframe => { let depth = 0, parent = iframe; while((parent = parent.parentNode)) { if(parent.tagName === 'IFRAME') depth++; } if(depth > CONFIG.protectionRules.maxFrameDepth) { AdUtils.safeRemove(iframe, 'FrameSystem', { type: '深层嵌套框架', detail: `嵌套层级: ${depth}` }); } const container = iframe.closest('div, section'); if(container && !AdUtils.isWhitelisted(container)) { AdUtils.safeRemove(container, 'FrameSystem', { type: '广告容器', detail: 'iframe父容器' }); } }); } checkThirdParty() { if(!Config.get('thirdPartyBlock')) return; document.querySelectorAll('script, iframe').forEach(el => { try { const src = new URL(el.src).hostname; const current = new URL(location.href).hostname; if(!src.endsWith(current)) { AdUtils.safeRemove(el, 'ThirdParty', { type: '第三方资源', detail: `源域: ${src}` }); } } catch {} }); } injectProtectionStyles() { GM_addStyle(` [style*="fixed"], [style*="sticky"] { position: static !important } iframe[src*="ad"] { display: none !important } .ad-shield-protected { border: 2px solid #4CAF50 !important; } `); } checkElements(selector, fn) { document.querySelectorAll(selector).forEach(fn); } } // ======================= 配置系统 ======================= class Config { static get currentDomain() { return location.hostname.replace(/^www\./, ''); } static get allKeys() { return Object.keys(CONFIG.defaultSettings); } static get(key) { const data = GM_getValue('config') || {}; const domainConfig = data[this.currentDomain] || {}; return domainConfig[key] ?? CONFIG.defaultSettings[key]; } static set(key, value) { const data = GM_getValue('config') || {}; data[this.currentDomain] = {...CONFIG.defaultSettings, ...data[this.currentDomain], [key]: value}; GM_setValue('config', data); } static toggleAll(status) { const data = GM_getValue('config') || {}; data[this.currentDomain] = Object.fromEntries( Config.allKeys.map(k => [k, status]) ); GM_setValue('config', data); } } // ======================= 关键词管理 ======================= class KeywordManager { static getStorageKey(type) { return `content_${type}_${Config.currentDomain}`; } static getBlacklist() { return this.getKeywords('blacklist'); } static getWhitelist() { return this.getKeywords('whitelist'); } static getKeywords(type) { const raw = GM_getValue(this.getStorageKey(type), ''); return this.parseKeywords(raw); } static parseKeywords(raw) { return raw.split(',') .map(k => k.trim()) .filter(k => k.length >= CONFIG.contentFilter.minLength) .slice(0, CONFIG.contentFilter.maxKeywords) .map(k => k.toLowerCase()); } static updateKeywords(type, keywords) { const valid = [...new Set(keywords)] .map(k => k.trim()) .filter(k => k.length >= CONFIG.contentFilter.minLength) .slice(0, CONFIG.contentFilter.maxKeywords); GM_setValue( this.getStorageKey(type), valid.join(',') ); } } // ======================= 用户界面 ======================= class UIController { static init() { this.registerMainMenu(); this.registerModuleCommands(); this.registerContentMenu(); this.registerUtilityCommands(); } static registerMainMenu() { const allEnabled = Config.allKeys.every(k => Config.get(k)); GM_registerMenuCommand( `🔘 主开关 [${allEnabled ? '✅' : '❌'}]`, () => this.toggleAllModules(!allEnabled) ); } static registerModuleCommands() { const modules = [ ['dynamicSystem', '动态检测系统 (ID/属性)'], ['layoutSystem', '布局检测系统 (定位/z-index)'], ['frameSystem', '框架过滤系统 (iframe)'], ['mediaSystem', '媒体检测系统 (图片/浮动)'], ['textSystem', '文本广告检测'], ['thirdPartyBlock', '第三方拦截'], ['contentFilter', '内容过滤系统'] ]; modules.forEach(([key, name]) => { GM_registerMenuCommand( `${name} [${Config.get(key) ? '✅' : '❌'}]`, () => this.toggleModule(key, name) ); }); } static registerContentMenu() { GM_registerMenuCommand('🔠 内容过滤管理', () => { GM_registerMenuCommand('➕ 添加黑名单关键词', () => this.handleAddKeyword('blacklist')); GM_registerMenuCommand('➕ 添加白名单关键词', () => this.handleAddKeyword('whitelist')); GM_registerMenuCommand('📋 显示当前关键词', () => this.showCurrentKeywords()); GM_registerMenuCommand('🗑️ 清除所有关键词', () => this.clearKeywords()); }); } static registerUtilityCommands() { GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs()); GM_registerMenuCommand('🧹 清除当前日志', () => Logger.clear()); GM_registerMenuCommand('⚙️ 重置所有配置', () => this.resetConfig()); } static toggleModule(key, name) { const value = !Config.get(key); Config.set(key, value); this.showNotification(`${name} ${value ? '✅ 已启用' : '❌ 已禁用'}`); setTimeout(() => location.reload(), 500); } static toggleAllModules(status) { Config.toggleAll(status); this.showNotification(`所有模块已${status ? '启用' : '禁用'}`); setTimeout(() => location.reload(), 500); } static handleAddKeyword(type) { const promptText = type === 'blacklist' ? '输入要屏蔽的关键词(支持中文):' : '输入要豁免的关键词:'; const input = prompt(promptText); if (!input) return; const current = KeywordManager.getKeywords(type); KeywordManager.updateKeywords(type, [...current, input]); this.showNotification( `已添加${type === 'blacklist' ? '黑' : '白'}名单关键词:${input}` ); } static showCurrentKeywords() { const black = KeywordManager.getBlacklist(); const white = KeywordManager.getWhitelist(); alert(`【当前内容过滤规则 - ${location.hostname}】 ■ 黑名单 (${black.length}个): ${black.join(', ') || '无'} ■ 白名单 (${white.length}个): ${white.join(', ') || '无'}`); } static clearKeywords() { if (!confirm('确定清除所有关键词吗?')) return; ['blacklist', 'whitelist'].forEach(type => { GM_setValue(KeywordManager.getStorageKey(type), ''); }); this.showNotification('已清除所有关键词'); } static resetConfig() { if (!confirm('确定重置所有配置吗?')) return; const data = GM_getValue('config') || {}; delete data[Config.currentDomain]; GM_setValue('config', data); this.showNotification('配置已重置'); setTimeout(() => location.reload(), 500); } static showLogs() { const logs = Logger.getLogs(); alert(logs.length ? `📃 最近${CONFIG.maxLogs}条拦截记录:\n\n${logs.map(l => `[${l.time}] ${l.module}\n类型: ${l.type}\n元素: ${l.element}` ).join('\n\n')}` : '暂无拦截记录' ); } static showNotification(text, duration = 2000) { GM_notification({ title: '广告终结者', text: text, silent: true, timeout: duration }); } } // ======================= 日志系统 ======================= class Logger { static logRemoval(data) { const logs = GM_getValue('logs', []); logs.push({ time: new Date().toLocaleTimeString(), module: data.module, type: data.reason.type, detail: data.reason.detail, element: `${data.element.tag}#${data.element.id}` }); GM_setValue('logs', logs.slice(-CONFIG.maxLogs)); } static getLogs() { return GM_getValue('logs', []); } static clear() { GM_setValue('logs', []); UIController.showNotification('日志已清空'); } } // ======================= 初始化 ======================= (function init() { new CoreSystem(); UIController.init(); })(); })();