查看: 92|回复: 0

如何编写油猴(tampermonkey)插件

[复制链接]

2

主题

7

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2023-7-21 13:04:57 | 显示全部楼层 |阅读模式
油猴插件是一个非常流行的浏览器插件,用于管理用户脚本(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 () {
    'use strict';
    // 当页面加载完成后再执行脚本,避免脚本重复执行,多次弹窗
    document.onreadystatechange = function () {
        if (document.readyState === "complete") {
            alert("hello world!");
        }
    }
})();



hello world

Step3: 编写入门插件:修改页面元素

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


要实现上面的目标,我们可以把需求拆分为两部分:数据解析和页面修改,下面分别介绍如何实现。
数据解析

通过观察网页,可以看到页面上已经渲染出图书标题、作者、出版社等信息,只是当前是按照条目单独展示的,而不是我们需要的表格形式。
打开控制台,我们能够看到每个图书条目是按照如下的数据结构组织的,
<ul class="subject-list">
  <li class="subject-item">
    // 图书条目1
  </li>
  <li class="subject-item">
    // 图书条目2
  </li>
  // ...
</ul>所以可以通过以下的方式遍历条目列表
let bookList = [];
let nodeList = document.querySelectorAll("li.subject-item");
nodeList.forEach(node => {
    // 解析条目信息
    bookList.push(条目信息);
})
每个图书条目的数据结构如下,标题内容可以直接访问a元素的文本内容来获取,而作者、出版社等信息是在同一个div元素中,只能通过正则表达式来进行解析。
<div class="info">
  <h2 class="">
    <a href="https://book.douban.com/subject/36104107/" title="长安的荔枝"
      onclick="moreurl(this,{i:'0',query:'',subject_id:'36104107',from:'book_subject_search'})">
      长安的荔枝
    </a>
  </h2>
  <div class="pub">
    马伯庸 / 湖南文艺出版社 / 2022-10 / 45.00元
  </div>
  // ...
</div>解析图书条目属性的代码如下
// let node = document.querySelectorAll("li.subject-item")[0]
let title = node.querySelector("div.info h2 a").innerText.trim();
let pubInfo = node.querySelector("div.info div.pub").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 () {
    'use strict';
    document.onreadystatechange = function () {
        if (document.readyState === "complete") {
            addMenuBar();
        }
    }
    // 在标题后添加工具栏按钮,并注册点击事件,点击后触发 showTableData 方法
    function addMenuBar() {
        var previewBtn = document.createElement("button");
        previewBtn.setAttribute("type", "button");
        previewBtn.setAttribute("id", "preview-button")
        previewBtn.addEventListener("click", (event) => {
            showDataTable();
        })
        previewBtn.append(document.createTextNode("查看表格数据"));
        var titleNode = document.querySelector("#content h1");
        titleNode.parentNode.insertBefore(previewBtn, titleNode.nextSibling);
    }

    // 调用 parseBookData,将解析好的数据渲染为表格数据,并插入到工具栏下
    function showDataTable() {
        let titleList = ["书名", "作者", "出版社", "出版日期", "价格"];
        let thead = document.createElement("thead");
        let row = thead.insertRow();
        titleList.forEach(title => {
            let cell = row.insertCell();
            cell.innerText = title;
        })
        let bookList = parseBookData();
        var tbody = document.createElement("tbody");
        bookList.forEach(book => {
            let row = tbody.insertRow();
            book.forEach(item => {
                row.insertCell().innerText = item;
            })
        })
        var table = document.createElement("table");
        table.setAttribute("border", 1);
        table.appendChild(thead);
        table.appendChild(tbody);

        // 将表格添加在 button 元素后
        var targetNode = document.querySelector("#preview-button");
        console.log("targetNode");
        targetNode.parentNode.insertBefore(table, targetNode.nextSibling);
    }

    // 解析当前网页的数据数据
    function parseBookData() {
        let bookList = [];
        let nodeList = document.querySelectorAll("li.subject-item");
        nodeList.forEach(node => {
            // let imgUrl = node.querySelector("div a img").getAttribute('src');
            let title = node.querySelector("div.info h2 a").innerText.trim();
            let pubInfo = node.querySelector("div.info div.pub").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: 编写插件:下载文件

待更新
参考资料


  • 文档 | Tampermonkey
回复

举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表