Chrome扩展开发实战:CSDN免关注阅读&破解复制插件

项目简介

这是一个针对CSDN博客平台的Chrome扩展,主要功能包括:

  • 解除”关注博主才能阅读全文”的限制
  • 破解文章复制限制
  • 自动清除各类弹窗广告
  • 优化阅读界面

核心文件结构

1
2
3
4
5
6
7
8
└─ CSDN取消关注限制
├─ manifest.json // 扩展配置文件
├─ background.js // 后台服务脚本
├─ content-script.js // 页面注入脚本
├─ popup.html // 弹出窗口界面
├─ popup.js // 弹出窗口逻辑
├─ style.css // 样式文件
└─ images // 图标资源

关键技术实现

1. 清单配置 (manifest.json)

manifest.json 是 Chrome 浏览器扩展的核心文件,包含了扩展的元数据、版本信息、所需权限、背景脚本、内容脚本等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
// 插件的名称
"name": "CNDS无需关注",

// 插件描述
"description": "解锁关注后查看限制",

// 插件的版本
"version": "0.1.1",

// 清单文件的版本,一般为3,2已经弃用
"manifest_version": 3,

// 图标,一般偷懒全部用一个尺寸的也没问题
"icons":
{
"16": "/images/icon.png",
"48": "/images/icon.png",
"128": "/images/icon.png"
},

// 会一直常驻的后台JS或后台页面
"background":
{
"service_worker": "./background.js"
},

// 浏览器右上角图标设置
"action":
{
"default_popup": "popup.html",
"default_icon ": "/images/icon.png",
"default_title": "CNDS无需关注"
},

// 权限申请
"permissions":
[
"storage",
"tabs",
"scripting",
"notifications"
],

//可以访问的网站
"host_permissions":
[
"<all_urls>",
"https://blog.csdn.net/"
]
}

完整的配置文档请戳这里

2. 后台服务 (background.js)

background.js 是扩展的后台脚本,它在扩展的生命周期内持续运行,不受页面加载和卸载的影响。它通常用于处理非页面特定的逻辑,如监听浏览器事件、跨页面通信、管理扩展的状态、存储数据等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//安装时默认启用
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.set({
Enabled: true, //启用扩展
Auto_expand: false, //自动展开
Enable_copy: true, //破解复制
Clear_doms: true, //清除弹窗
}, () => {
console.log('OK: Extension is installed!');
});
});

//监听tab页面加载,正则匹配CSDN网址
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (/blog.csdn.net/.test(tab.url) && /article/.test(tab.url) && changeInfo.status === "complete") {
chrome.scripting.executeScript({
target: {
tabId: tabId
},
files: ["./content-script.js"],
}).then(() => {
console.log("INJECTED SCRIPT SUCC.");
}).catch((err) => console.log(err));
}
});

3. 页面注入脚本 (content-script.js)

content-script.js是内容脚本,它会被注入到匹配的页面上下文中执行。内容脚本可以直接访问和操作页面的DOM,因此它们通常用于修改页面内容、监听用户交互、与页面JavaScript交互等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
chrome.storage.local.get(['Enabled', 'Auto_expand', 'Enable_copy', 'Clear_doms'], function(value) {
if (value.Enabled) {
console.log("扩展开始执行");

if (value.Enable_copy) {
console.log("执行破解复制");
//enableCopy(); //启用复制
enableCopy2();
}

console.log("执行破解关注");
if (value.Auto_expand) {
expand(); //自动展开
} else {
modify(); //修改防止默认跳转
}

if (value.Clear_doms) {
console.log("执行清除弹窗");
removeDoms(); //移除登录弹窗等

//创建新的dom监听器
let observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type == 'childList') { //只监听一级
removeDoms();
}
}
});
// 开始监听属性变化
observer.observe(document.body, {
childList: true
});
// beforeDestory(() => {
// observer.disconnect(); //取消对所有dom的监听
// observer = null;
// });
}
} else {
console.log("未启用扩展功能");
}
});

function modify() {
let follow_text = document.getElementsByClassName('follow-text');
if (follow_text.length == 0) {
return;
}
follow_text = follow_text[0];
if (follow_text.textContent == "关注博主即可阅读全文") {
follow_text.addEventListener("click", (event) => {
event.stopPropagation(); //禁止默认跳转
expand(); //展开
});
let img = document.getElementsByClassName('chevrondown')[0]; //"关注博主即可阅读全文"旁向下的箭头图标
img.addEventListener("click", (event) => {
event.stopPropagation(); //禁止默认跳转
expand(); //展开
});
follow_text.textContent = "关注限制已解锁";
console.log("关注限制已解锁");
} else {
console.log("没有找到\"关注博主即可阅读全文\"");
}
}

function expand() {
let article_content = document.getElementById("article_content");
let hide_article_box = document.getElementsByClassName('hide-article-box')[0];
article_content.removeAttribute("style");
hide_article_box.parentElement.removeChild(hide_article_box);
console.log("全文展开成功");
}

function removeDoms() {
let classes = ['passport-login-tip-container false', 'passport-login-container', 'tool-active-list', 'article-search-tip',
'hljs-button signin active', 'csdn-side-toolbar', 'box-shadow mb8', 'blog-footer-bottom'];
let infos = ['右下角弹窗', '登录弹窗', '一键收藏', '黑色提示框',
'登录复制提示', '侧边工具栏', '左侧广告', '底部备案信息'];
let boxs, box;
for (i = 0; i < classes.length; i++) {
boxs = document.getElementsByClassName(classes[i]);
if (boxs.length > 0) {
box = boxs[0];
box.parentElement.removeChild(box);
console.log("移除" + infos[i]);
}
}

let ids = ['toolbarBox', 'asideWriteGuide', 'asideNewNps', 'recommendNps'];
let names = ["顶部栏", '左侧广告', '左侧是否推荐','底部是否推荐'];
for (i = 0; i < ids.length; i++) {
box = document.getElementById(ids[i]);
if(box == null) continue;
box.parentElement.removeChild(box);
console.log("移除" + names[i]);
}
}

// 参考:https://www.cnblogs.com/pu369/p/17425742.html
function enableCopy() {
function setAllSelect(el = document.body) {
for (let index = 0; index < el.children.length; index++) {
const e = el.children.item(index);
e.style.userSelect = 'text';
setAllSelect(e);
}
}
setAllSelect();

//监听键盘Ctrl+C
document.body.onkeydown = function(e) {
if (e.ctrlKey && e.keyCode == 67) {
const pasteText = window.getSelection().toString();
if (null === pasteText || undefined === pasteText || '' === pasteText.trim()) {
return;
}
navigator.clipboard.writeText(pasteText).then(() => {
console.log("复制成功!"); //alert("复制失败!");
}).catch(() => {
console.log("复制失败!");
});
}
}
//监听右键复制无法实现
}

// 参考:https://blog.csdn.net/weixin_49119066/article/details/139220307
function enableCopy2() {
window.oncontextmenu = document.oncontextmenu = document.oncopy = null;
var hea = document.getElementsByClassName('toolbar-advert')[0];
if (hea) {
hea.remove()
};
[...document.querySelectorAll('body')].forEach(dom => dom.outerHTML = dom.outerHTML);
[...document.querySelectorAll('body, body *')].forEach(dom => {
['onselect', 'onselectstart', 'onselectend', 'ondragstart', 'ondragend', 'oncontextmenu', 'oncopy'].forEach(ev => dom.removeAttribute(ev));
dom.style['user-select'] = 'auto';
});
}

4. 插件配置页面与样式文件 (popup.html & style.css)

popup.html 通常是一个与浏览器动作(Browser Action)或页面动作(Page Action)关联的弹出窗口的HTML文件。当用户点击扩展时,将显示弹出窗口。用户可在该窗口进行相关设置。

popup.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div align="center"><font size=3 face="黑体">CSDN插件设置</font></div>
<div class="option" align="center">
<span class="name">开启插件:</span>
<label class="switch">
<input type="checkbox" id="switch-enable" class="checkbox">
</label>
</div>
<div class="option" align="center">
<span class="name">自动展开:</span>
<label class="switch">
<input type="checkbox" id="switch-autoexpand" class="checkbox">
</label>
</div>
<div class="option" align="center">
<span class="name">破解复制:</span>
<label class="switch">
<input type="checkbox" id="switch-enablecopy" class="checkbox">
</label>
</div>
<div class="option" align="center">
<span class="name">清除弹窗:</span>
<label class="switch">
<input type="checkbox" id="switch-clearDom" class="checkbox">
</label>
</div>
<div align="center"><button id="refresh">刷新网页</button></div>
<script src="popup.js"></script>
</body>
</html>

style.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
html,body{
margin: 0px;
width: 200px;
height: 100px;
font-family: 'Courier New', Courier, monospace;
}

.name{
position: relative;
margin: 10px 10px 10px 10px;
}
.checkbox{
position: relative;
margin: 10px 10px 10px 10px;
}

5. 插件配置页面脚本 (popup.js)

popup.js通常与popup.html一起使用,用于处理弹出窗口(Popup)中的用户交互和动态行为。当用户点击浏览器动作(Browser Action)或页面动作(Page Action)的图标时,会打开popup.html,而popup.js则负责为其中的元素添加事件监听器、处理用户输入以及与其他部分(如背景脚本或内容脚本)进行通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
const checkBoxs = document.getElementsByClassName("checkbox");

//获取各个选项值
chrome.storage.local.get(['Enabled', 'Auto_expand', 'Enable_copy', 'Clear_doms'], function(value) {
checkBoxs[0].checked = value.Enabled;
checkBoxs[1].checked = value.Auto_expand;
checkBoxs[2].checked = value.Enable_copy;
checkBoxs[3].checked = value.Clear_doms;
});

//启用插件
checkBoxs[0].addEventListener("change", () => {
if (checkBoxs[0].checked) {
console.log("启用扩展");
chrome.storage.local.set({
Enabled: true
});
} else {
console.log("关闭扩展");
chrome.storage.local.set({
Enabled: false
});
}

//刷新网页
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
chrome.scripting.executeScript({
target: {
tabId: tabs[0].id
},
func: refreshPage,
});
});
});

//自动展开
checkBoxs[1].addEventListener("change", () => {
if (checkBoxs[1].checked) {
console.log("启用自动展开");
chrome.storage.local.set({
Auto_expand: true
});
} else {
console.log("关闭自动展开");
chrome.storage.local.set({
Auto_expand: false
});
}
});

//破解复制
checkBoxs[2].addEventListener("change", () => {
if (checkBoxs[2].checked) {
console.log("启用破解复制");
chrome.storage.local.set({
Enable_copy: true
});
} else {
console.log("关闭破解复制");
chrome.storage.local.set({
Enable_copy: false
});
}
});

//清除弹窗
checkBoxs[3].addEventListener("change", () => {
if (checkBoxs[3].checked) {
console.log("启用清除弹窗");
chrome.storage.local.set({
Clear_doms: true
});
} else {
console.log("关闭清除弹窗");
chrome.storage.local.set({
Clear_doms: false
});
}
});

document.getElementById("refresh").onclick = () => {
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
chrome.scripting.executeScript({
target: {
tabId: tabs[0].id
},
func: refreshPage,
});
});
}

function refreshPage() {
window.location.reload();
}

使用说明

  1. 下载扩展文件
  2. 在Chrome地址栏输入chrome://extensions/
  3. 开启”开发者模式”
  4. 点击”加载已解压的扩展程序”选择项目文件夹

完整代码下载 密码:9dq3

注意:本扩展仅用于学习Chrome扩展开发,请勿用于商业用途


Chrome扩展开发实战:CSDN免关注阅读&破解复制插件
https://blog.cngo.xyz/posts/716.html
作者
cngo
发布于
2024年6月24日
许可协议