想必大家在网上搜索解决问题解决方案时都会有这样的经历,明明找到了想要的解决问题的代码,想要一键复制到项目代码中,但有些网页限制了你的复制行为,可能让你登录账号、关注博主,更有甚者直接收费,其中比如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+ 版本的标准操作方式
- 进入管理页:在 Chrome 右上角点击拼图图标,找到 Tampermonkey,点击右侧的 三个点,选择 "管理扩展程序"
- 查找开关:在 Tampermonkey 的详情页面里,向下滑动,找到 "允许用户脚本" 的开关。
- 开启:将其打开,然后完全重启一次 Chrome 浏览器
方法二:在控制台强制执行(如果方法一无效)
如果在详情页找不到上述开关,或者开启后依然无效,可以尝试用命令强制激活
-
确保开发者模式已开:在地址栏输入
chrome://extensions/并回车,确认右上角的 "开发者模式" 处于开启状态 -
粘贴命令:按
F12打开开发者工具,切换到 Console(控制台) 标签页,将以下代码完整粘贴进去并回车:javascript
chrome.developerPrivate.updateExtensionConfiguration({ extensionId: "dhdgffkkebhmkfjojejmpbldmpobfkfo", userScriptsAccess: true }); -
重启:执行完毕后,完全关闭 Chrome 并重新打开,再刷新网页试试
直接运行
使用 "代码片段" 保存终极脚本(一劳永逸)
如果你经常需要面对复杂的复制限制(比如你之前提到的 <pre> 标签问题),每次在控制台输入一大段代码会很麻烦。Chrome 的 "代码片段(Snippets)" 功能可以完美解决这个问题,相当于一个内置的、轻量级的脚本管理器。
-
打开Snippets面板:
- 打开开发者工具(
F12)。 - 切换到 Sources(源代码) 标签页。
- 打开开发者工具(
-
创建并保存脚本:
- 点击 "New snippet"(新建代码片段) 。
- 将我之前为你提供的那个增强版终极脚本(包含了捕获阶段拦截、克隆替换
<pre><code>标签、处理 Shadow DOM 等功能的完整代码)粘贴到右侧的编辑器中。 - 按
Ctrl+S保存,你可以给它起个名字,比如终极复制解锁。
-
运行脚本:
- 以后在任何遇到复制限制的网页,只需打开开发者工具 -> Sources -> Snippets,找到你保存的脚本,右键点击并选择 "Run"(运行) ,或者点击右下角的运行按钮(一个类似播放键的图标)即可。
这样,你就拥有了一个不需要 Tampermonkey 也能随时调用的 "终极解锁工具"。