终于不用看到CSDN该死的弹窗限制了

0 阅读4分钟

想必大家在网上搜索解决问题解决方案时都会有这样的经历,明明找到了想要的解决问题的代码,想要一键复制到项目代码中,但有些网页限制了你的复制行为,可能让你登录账号、关注博主,更有甚者直接收费,其中比如CSDN,所以秉着互联网的开源精神我写个脚本来解决这问题,但由于个游览器的差异本文只针对我经常使用的Chrome游览器进行讲解。

开发脚本

根据对CSDN网页结构的了解我们主要针对包裹代码的<pre> 和 <code> 标签,还有网页对用户复制操作的监听的处理。

// ==UserScript==
// @name         终极复制解锁(捕获阶段拦截 + 全面清理)
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  彻底解除复制限制,包括 pre > code 无法复制的问题
// @author       You
// @match        *://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // 需要拦截的事件类型(这些事件常被用于限制复制)
    const BLOCKED_EVENTS = [
        'copy', 'cut', 'selectstart', 'contextmenu', 'selectionchange', 'mousedown', 'mouseup'
    ];
    console.log(`脚本启动`);
    // ---------- 1. 捕获阶段拦截事件传播,但不阻止默认行为 ----------
    function stopPropagationOnly(e) {
        e.stopPropagation(); // 阻止事件传播到页面注册的监听器
        // 不调用 preventDefault,保证浏览器的默认复制行为能执行
    }

    BLOCKED_EVENTS.forEach(eventType => {
        document.addEventListener(eventType, stopPropagationOnly, true); // 捕获阶段
        window.addEventListener(eventType, stopPropagationOnly, true);
    });

    // ---------- 2. 拦截后续 addEventListener 调用 ----------
    const originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
        if (BLOCKED_EVENTS.includes(type)) {
            console.log(`[解锁脚本] 已阻止添加事件: ${type}`);
            return; // 忽略添加
        }
        return originalAddEventListener.call(this, type, listener, options);
    };

    // ---------- 3. 清除已有事件属性和 CSS 限制 ----------
    function removeRestrictions(root) {
        if (!root) return;
        // 兼容 iframe 传入的 window
        if (root.document) root = root.document;

        // 清理 document 对象
        if (root.nodeType === Node.DOCUMENT_NODE) {
            root.oncopy = null;
            root.oncut = null;
            root.onselectstart = null;
            root.oncontextmenu = null;
            root.onselectionchange = null;

            // 添加强制选择样式
            const style = root.createElement('style');
            style.innerHTML = `
                * {
                    user-select: text !important;
                    -webkit-user-select: text !important;
                    -moz-user-select: text !important;
                    -ms-user-select: text !important;
                }
            `;
            root.head.appendChild(style);
        } else {
            // 普通元素
            root.oncopy = null;
            root.oncut = null;
            root.onselectstart = null;
            root.oncontextmenu = null;
            root.onselectionchange = null;
        }

        // 处理 Shadow DOM
        if (root.shadowRoot) {
            removeRestrictions(root.shadowRoot);
        }

        // 遍历所有后代元素
        if (root.querySelectorAll) {
            root.querySelectorAll('*').forEach(el => {
                el.oncopy = null;
                el.oncut = null;
                el.onselectstart = null;
                el.oncontextmenu = null;
                el.onselectionchange = null;
                if (el.shadowRoot) {
                    removeRestrictions(el.shadowRoot);
                }
            });
        }
    }

    // ---------- 4. 专门处理 pre 和 code 标签:克隆替换以移除所有事件监听器 ----------
    function unlockElements(root) {
        const selectors = ['pre', 'code'];
        selectors.forEach(selector => {
            const elements = root.querySelectorAll(`${selector}:not([data-unlocked])`);
            elements.forEach(el => {
                try {
                    const clone = el.cloneNode(true);
                    clone.setAttribute('data-unlocked', 'true');
                    el.parentNode.replaceChild(clone, el);
                    console.log(`[解锁脚本] 已替换 ${selector} 标签,移除了所有事件监听器`);
                } catch (e) {
                    console.warn(`[解锁脚本] 替换 ${selector} 失败`, e);
                }
            });
        });
    }

    // 初始执行
    removeRestrictions(document);
    unlockElements(document);

    // ---------- 5. 观察动态添加的内容 ----------
    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    removeRestrictions(node);
                    unlockElements(node);
                }
            });
        });
    });
    if (document.body) {
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // ---------- 6. 处理 iframe ----------
    function unlockIframes(root) {
        root.querySelectorAll('iframe').forEach(iframe => {
            try {
                const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                if (iframeDoc) {
                    removeRestrictions(iframeDoc);
                    unlockElements(iframeDoc);
                    // 为 iframe 建立独立的观察者
                    const iframeObserver = new MutationObserver(() => {
                        removeRestrictions(iframeDoc);
                        unlockElements(iframeDoc);
                    });
                    if (iframeDoc.body) {
                        iframeObserver.observe(iframeDoc.body, { childList: true, subtree: true });
                    }
                }
            } catch(e) {
                // 跨域 iframe 忽略
            }
        });
    }

    // 定期扫描 iframe 和重复清理(防止页面动态重置)
    setInterval(() => {
        unlockIframes(document);
        removeRestrictions(document);
        unlockElements(document);
    }, 2000);
})();

运行脚本

脚本开发完了就到了实战的时候了,要在游览器中运行写好的脚本文件,以下提供您两种方式,以下方法方案都需要用户打开Chrome的开发者模式方能使用。

打开Chrome的开发者模式 打开Chrome的开发者模式 打开Chrome的开发者模式 !!!重要的事情说三遍

工具运行

使用Chrome扩展工具 在Chrome扩展应用商城下载Tampermonkey,操作Tampermonkey新建脚本,将之前写的脚本代码放入新建的脚本文件中,保存完毕后使用以下方法在网页中运行

方法一:通过扩展管理页面开启 "允许用户脚本"

这是 Chrome 138+ 版本的标准操作方式

  1. 进入管理页:在 Chrome 右上角点击拼图图标,找到 Tampermonkey,点击右侧的 三个点,选择  "管理扩展程序"
  2. 查找开关:在 Tampermonkey 的详情页面里,向下滑动,找到  "允许用户脚本"  的开关。
  3. 开启:将其打开,然后完全重启一次 Chrome 浏览器 

方法二:在控制台强制执行(如果方法一无效)

如果在详情页找不到上述开关,或者开启后依然无效,可以尝试用命令强制激活 

  1. 确保开发者模式已开:在地址栏输入 chrome://extensions/ 并回车,确认右上角的  "开发者模式"  处于开启状态 

  2. 粘贴命令:按 F12 打开开发者工具,切换到 Console(控制台)  标签页,将以下代码完整粘贴进去并回车:

    javascript

    chrome.developerPrivate.updateExtensionConfiguration({
        extensionId: "dhdgffkkebhmkfjojejmpbldmpobfkfo",
        userScriptsAccess: true
    });
    
  3. 重启:执行完毕后,完全关闭 Chrome 并重新打开,再刷新网页试试 

直接运行

使用 "代码片段" 保存终极脚本(一劳永逸)

如果你经常需要面对复杂的复制限制(比如你之前提到的 <pre> 标签问题),每次在控制台输入一大段代码会很麻烦。Chrome 的  "代码片段(Snippets)"  功能可以完美解决这个问题,相当于一个内置的、轻量级的脚本管理器。

  1. 打开Snippets面板

    • 打开开发者工具(F12)。
    • 切换到 Sources(源代码)  标签页。
  2. 创建并保存脚本

    • 点击  "New snippet"(新建代码片段)
    • 将我之前为你提供的那个增强版终极脚本(包含了捕获阶段拦截、克隆替换 <pre><code> 标签、处理 Shadow DOM 等功能的完整代码)粘贴到右侧的编辑器中。
    • 按 Ctrl+S 保存,你可以给它起个名字,比如 终极复制解锁
  3. 运行脚本

    • 以后在任何遇到复制限制的网页,只需打开开发者工具 -> Sources -> Snippets,找到你保存的脚本,右键点击并选择  "Run"(运行) ,或者点击右下角的运行按钮(一个类似播放键的图标)即可。

这样,你就拥有了一个不需要 Tampermonkey 也能随时调用的 "终极解锁工具"。