Greasy Fork is available in English.

屏蔽 CSDN

屏蔽 CSDN 及其它垃圾信息

// ==UserScript==
// @name        屏蔽 CSDN
// @version    1.1.2
// @author      aaron yan
// @description 屏蔽 CSDN 及其它垃圾信息
// @match       https://www.baidu.com
// @match       https://www.baidu.com/s*
// @match       https://www.google.com
// @match       https://www.google.com/search*
// @match       https://www.google.com.hk/search*
// @match       https://www.google.com.tw/search*
// @grant       GM_xmlhttpRequest

// @license     MIT

// @changelog    v1.1.0 (2024-01-17)
//              1. 优化代码结构和性能
//              2. 改进屏蔽规则管理
//              3. 去除延时响应
// @namespace https://greasyfork.org/users/1249199
// ==/UserScript==

(function () {
    // 配置对象
    const CONFIG = {
        // 搜索引擎配置
        engines: {
            baidu: {
                host: 'baidu.com',
                container: '#content_left',
                selectors: {
                    title: ['h3.c-title', '.t', '.tts-title', '.c-gap-right '],
                    url: '.c-showurl',
                    result: '.result.c-container, .c-container.new-pmd',
                    source: 'div[class*="source"]',
                    searchButton: '#su, input[type="submit"]'  // 百度搜索按钮
                }
            },
            google: {
                host: 'google.com',
                container: '#search',
                selectors: {
                    title: ['.VuuXrf'],
                    result: '.MjjYud',
                    searchButton: 'input[name="btnK"], input[type="submit"]'  // 谷歌搜索按钮
                }
            }
        },

        // 屏蔽规则配置
        blockRules: {
            // 技术网站
            tech: [
                "CSDN博客", "CSDN技术社区", "csdn.net",
                "简书", "博客园", "PHP中文网",
                "Worktile", "慕课网", "知了爱学", "51CTO", "腾讯云计算",
                "360Doc", "千锋教育", "筋斗云"
            ],
            // 下载站点
            download: [
                "软件园", "下载之家", "下载网",
                "华军软件园", "当下软件园", "东坡下载站",
                "系统之家", "/soft/"
            ],
            // 医疗健康
            health: [
                "百度健康", "快速问医生", "求医网",
                "求医问药", "家庭医生", "寻医",
                "健康", "健客网", "医生"
            ],
            // 其他
            others: [
                "亿速云", "动力节点在线", "IT 技术博客",
                "千锋教育", "虎课网", "黑马程序员", "抖音"
            ]
        },

        // URL屏蔽规则
        blockUrls: [
            'douyin.com'
        ]
    };

    // 工具函数
    const utils = {
        // 防抖函数
        debounce(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func(...args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        },

        // 日志函数
        log(type, message, data = {}) {
            const styles = {
                block: 'color: #ff6b6b; font-weight: bold',
                info: 'color: #4CAF50; font-weight: bold',
                error: 'color: #f44336; font-weight: bold'
            };
            console.log(`%c ${message}`, styles[type]);
        },

        // 获取当前搜索引擎
        getCurrentEngine() {
            const host = window.location.hostname;
            return Object.entries(CONFIG.engines).find(([, config]) =>
                host.includes(config.host)
            )?.[0];
        },

        // 获取所有屏蔽关键词
        getAllBlockedSites() {
            return Object.values(CONFIG.blockRules)
                .flat()
                .filter((value, index, self) => self.indexOf(value) === index);
        }
    };

    // 搜索结果处理类
    class SearchResultHandler {
        constructor(engine) {
            this.engine = engine;
            this.config = CONFIG.engines[engine];
            this.blockedSites = utils.getAllBlockedSites();
        }

        // 检查是否应该屏蔽
        shouldBlock(element) {
            // 检查标题
            const titleSelectors = this.config.selectors.title;
            for (const selector of titleSelectors) {
                const titleElement = element.querySelector(selector);
                if (titleElement && this.blockedSites.some(site =>
                    titleElement.textContent.trim().includes(site))) {
                    return true;
                }
            }

            // 检查URL(如果有URL选择器)
            if (this.config.selectors.url) {
                const urlElement = element.querySelector(this.config.selectors.url);
                if (urlElement && CONFIG.blockUrls.some(url =>
                    urlElement.textContent.trim().includes(url))) {
                    return true;
                }
            }

            // 检查来源(如果有source选择器)
            if (this.config.selectors.source) {
                const sourceElement = element.querySelector(this.config.selectors.source);
                if (sourceElement && this.blockedSites.some(site =>
                    sourceElement.textContent.trim().includes(site))) {
                    return true;
                }
            }

            return false;
        }

        // 移除搜索结果
        removeBlockedSites() {
            try {
                const results = document.querySelectorAll(this.config.selectors.result);
                results.forEach(result => {
                    if (this.shouldBlock(result)) {
                        const title = result.querySelector(this.config.selectors.title[0])?.textContent.trim();
                        const source = result.querySelector(this.config.selectors.source)?.textContent.trim();
                        utils.log('block', 'Blocked:', {
                            title,
                            source: source || 'N/A'
                        });
                        result.remove();
                    }
                });
            } catch (error) {
                utils.log('error', `[${this.engine}] Error:`, error);
            }
        }
    }

    // 观察器类
    class ResultObserver {
        constructor() {
            this.engine = utils.getCurrentEngine();
            if (!this.engine) return;

            this.handler = new SearchResultHandler(this.engine);
            this.container = document.querySelector(CONFIG.engines[this.engine].container);
            this.observer = null;
        }

        init() {
            if (!this.container) return;

            this.observer = new MutationObserver(() => this.handler.removeBlockedSites());
            this.observer.observe(this.container, {
                childList: true,
                subtree: true
            });

            window.addEventListener('scroll',
                () => this.handler.removeBlockedSites(),
                { passive: true }
            );

            this.handler.removeBlockedSites();
        }

        reset() {
            if (this.observer) {
                this.observer.disconnect();
            }
            this.init();
        }
    }

    // 初始化
    function init() {
        utils.log('info', '⚡ Content Blocker Activated! ⚡');
        
        let observer = null;
        let lastUrl = location.href;

        function tryInit() {
            observer = new ResultObserver();
            if (observer.container) {
                observer.init();
            } else {
                setTimeout(tryInit, 100);
            }
        }

        tryInit();

        // 监听 URL 变化
        setInterval(() => {
            if (lastUrl !== location.href) {
                lastUrl = location.href;
                if (observer) {
                    observer.reset();
                } else {
                    tryInit();
                }
            }
        }, 1000);

        // 监听表单提交事件
        document.addEventListener('submit', (e) => {
            if (e.target.matches('form')) {
                setTimeout(() => {
                    if (observer) {
                        observer.reset();
                    } else {
                        tryInit();
                    }
                }, 500);
            }
        });

        // 监听回车键
        document.addEventListener('keypress', (e) => {
            if (e.key === 'Enter' && (e.target.matches('input[type="text"]') || e.target.matches('input[type="search"]'))) {
                setTimeout(() => {
                    if (observer) {
                        observer.reset();
                    } else {
                        tryInit();
                    }
                }, 500);
            }
        });
    }

    // 立即执行初始化
    init();
})();