|
油猴插件是一个非常流行的浏览器插件,用于管理用户脚本(UserScript)。用户脚本是小型 JavaScript 程序,可用于向网页添加新功能或修改现有功能。常见的使用场景有调整网页样式、下载视频、图片、解析网盘资源、屏蔽广告等等。
油猴有多个版本:Greasemonkey、tampermonkey、violentmonkey等,本文只讨论tampermonkey插件。该插件支持多种浏览器,如Chrome、Firefox、Edge等。用户在安装插件后,可以从社区网站Greasy Fork来检索到自己需要的脚本。在使用社区脚本时需要注意安全问题,不要下载和安装来源不明的脚本,以免受到恶意脚本的攻击。
本文侧重于介绍如何编写一个油猴脚本。油猴的核心工作原理是向网页注入一段用户自定义的JavaScript脚本,所以编写油猴插件的核心就是编写这些脚本。
Step1. 前置准备:安装浏览器插件
以edge浏览器为例,进入edge扩展商店,搜索tampermonkey,进入到扩展主页点击安装即可。

tampermonkey扩展主页
Step2. 编写脚本:hello world
点击Tampermonkey扩展图片,点击添加新脚本,会弹窗一个编辑页面,在此页面编写代码即可。

在初始代码中我们能够看到一些 @xxx 的注释,这些是指令代码,具体作用可以参考下表。当然了,最好还是要去查看Tampermonkey官方文档。
字段 | 含义 | @name | 脚本的名称,如上图中的“插件示例” | @namespace | 脚本的命名空间 | @version | 脚本的版本号,用于检测是否需要更新脚本 | @description | 简介扼要的功能介绍 | @author | 脚本的作者名 | @match | 定义脚本可以在哪些网页上运行,匹配模式为
// @match <protocol>://<domain></path>
protocol为协议,domain为域名,path为页面路径,可以使用绝对值,也可以使用 * 实现模糊匹配,举例如下
// @match http://*/foo*
// @match http*://http://xx.com/foo*bar | @icon | 脚本的图标 | @grant | 用于添加 GM_* 和 GM.* 方法的白名单 | @require | 用于加载js资源文件,用户脚本会在资源加载完成后执行
举例
// @require https://code.jquery.com/jquery-2.1.4.min.js | 先写一个hello world, 进入到任何页面都会弹出提示“hello world!”
// ==UserScript==
// @name hello world!
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match http*://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function () {
&#39;use strict&#39;;
// 当页面加载完成后再执行脚本,避免脚本重复执行,多次弹窗
document.onreadystatechange = function () {
if (document.readyState === &#34;complete&#34;) {
alert(&#34;hello world!&#34;);
}
}
})();

hello world
Step3: 编写入门插件:修改页面元素
编写脚本时需要注意:了解目标网站的结构和元素,以便能够正确地解析和操作他们。 目标:在标题《豆瓣图书标签:小说》下,添加蓝色框图中的图书信息表格;首先展示一个菜单按钮“查看表格数据”,点击后解析页面数据渲染出表格元素。

要实现上面的目标,我们可以把需求拆分为两部分:数据解析和页面修改,下面分别介绍如何实现。
数据解析
通过观察网页,可以看到页面上已经渲染出图书标题、作者、出版社等信息,只是当前是按照条目单独展示的,而不是我们需要的表格形式。
打开控制台,我们能够看到每个图书条目是按照如下的数据结构组织的,
<ul class=&#34;subject-list&#34;>
<li class=&#34;subject-item&#34;>
// 图书条目1
</li>
<li class=&#34;subject-item&#34;>
// 图书条目2
</li>
// ...
</ul>所以可以通过以下的方式遍历条目列表
let bookList = [];
let nodeList = document.querySelectorAll(&#34;li.subject-item&#34;);
nodeList.forEach(node => {
// 解析条目信息
bookList.push(条目信息);
})
每个图书条目的数据结构如下,标题内容可以直接访问a元素的文本内容来获取,而作者、出版社等信息是在同一个div元素中,只能通过正则表达式来进行解析。
<div class=&#34;info&#34;>
<h2 class=&#34;&#34;>
<a href=&#34;https://book.douban.com/subject/36104107/&#34; title=&#34;长安的荔枝&#34;
onclick=&#34;moreurl(this,{i:&#39;0&#39;,query:&#39;&#39;,subject_id:&#39;36104107&#39;,from:&#39;book_subject_search&#39;})&#34;>
长安的荔枝
</a>
</h2>
<div class=&#34;pub&#34;>
马伯庸 / 湖南文艺出版社 / 2022-10 / 45.00元
</div>
// ...
</div>解析图书条目属性的代码如下
// let node = document.querySelectorAll(&#34;li.subject-item&#34;)[0]
let title = node.querySelector(&#34;div.info h2 a&#34;).innerText.trim();
let pubInfo = node.querySelector(&#34;div.info div.pub&#34;).textContent.trim();
// 出版社字段原文内容: 马伯庸 / 湖南文艺出版社 / 2022-10 / 45.00元
const regexpSize = /^(.+)\/(.+?)\/(.+?)\/(.+?)$/;
const match = pubInfo.match(regexpSize);
let author = match[1];
let publisher = match[2];
let pubDate = match[3];
let price = match[4];
页面修改
第一步需要在页面标题下添加一个button按钮,并注册click事件:将解析得到的数据渲染为表格的形式,添加在button按钮后。
此步代码略过。
代码汇总
// ==UserScript==
// @name 豆瓣图书-获取图书信息表格
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://book.douban.com/tag/*
// @icon none
// @grant none
// ==/UserScript==
(function () {
&#39;use strict&#39;;
document.onreadystatechange = function () {
if (document.readyState === &#34;complete&#34;) {
addMenuBar();
}
}
// 在标题后添加工具栏按钮,并注册点击事件,点击后触发 showTableData 方法
function addMenuBar() {
var previewBtn = document.createElement(&#34;button&#34;);
previewBtn.setAttribute(&#34;type&#34;, &#34;button&#34;);
previewBtn.setAttribute(&#34;id&#34;, &#34;preview-button&#34;)
previewBtn.addEventListener(&#34;click&#34;, (event) => {
showDataTable();
})
previewBtn.append(document.createTextNode(&#34;查看表格数据&#34;));
var titleNode = document.querySelector(&#34;#content h1&#34;);
titleNode.parentNode.insertBefore(previewBtn, titleNode.nextSibling);
}
// 调用 parseBookData,将解析好的数据渲染为表格数据,并插入到工具栏下
function showDataTable() {
let titleList = [&#34;书名&#34;, &#34;作者&#34;, &#34;出版社&#34;, &#34;出版日期&#34;, &#34;价格&#34;];
let thead = document.createElement(&#34;thead&#34;);
let row = thead.insertRow();
titleList.forEach(title => {
let cell = row.insertCell();
cell.innerText = title;
})
let bookList = parseBookData();
var tbody = document.createElement(&#34;tbody&#34;);
bookList.forEach(book => {
let row = tbody.insertRow();
book.forEach(item => {
row.insertCell().innerText = item;
})
})
var table = document.createElement(&#34;table&#34;);
table.setAttribute(&#34;border&#34;, 1);
table.appendChild(thead);
table.appendChild(tbody);
// 将表格添加在 button 元素后
var targetNode = document.querySelector(&#34;#preview-button&#34;);
console.log(&#34;targetNode&#34;);
targetNode.parentNode.insertBefore(table, targetNode.nextSibling);
}
// 解析当前网页的数据数据
function parseBookData() {
let bookList = [];
let nodeList = document.querySelectorAll(&#34;li.subject-item&#34;);
nodeList.forEach(node => {
// let imgUrl = node.querySelector(&#34;div a img&#34;).getAttribute(&#39;src&#39;);
let title = node.querySelector(&#34;div.info h2 a&#34;).innerText.trim();
let pubInfo = node.querySelector(&#34;div.info div.pub&#34;).textContent.trim();
// 出版社字段原文内容: 马伯庸 / 湖南文艺出版社 / 2022-10 / 45.00元
const regexpSize = /^(.+)\/(.+?)\/(.+?)\/(.+?)$/;
const match = pubInfo.match(regexpSize);
let author = match[1];
let publisher = match[2];
let pubDate = match[3];
let price = match[4];
bookList.push([title, author, publisher, pubDate, price]);
})
return bookList;
}
})();
效果预览

Step4: 编写插件:发起网络请求
待更新
Step5: 编写插件:下载文件
待更新
参考资料
|
|