主頁(yè) > 知識(shí)庫(kù) > 油猴腳本編寫教程詳解

油猴腳本編寫教程詳解

熱門標(biāo)簽:服務(wù)器配置 鐵路電話系統(tǒng) 網(wǎng)站文章發(fā)布 銀行業(yè)務(wù) 檢查注冊(cè)表項(xiàng) 呼叫中心市場(chǎng)需求 智能手機(jī) 美圖手機(jī)

油猴腳本(Tampermonkey)是一個(gè)非常流行的瀏覽器擴(kuò)展,它可以運(yùn)行由廣大社區(qū)編寫的擴(kuò)展腳本,來實(shí)現(xiàn)各式各樣的功能,常見的去廣告、修改樣式文件、甚至是下載視頻。今天我們就來看看如何編寫自己的油猴腳本。當(dāng)然為了運(yùn)行油猴腳本,你應(yīng)該在瀏覽器中安裝油猴插件。

安裝油猴插件

安裝油猴插件非常簡(jiǎn)單,直接在瀏覽器的擴(kuò)展商店中安裝即可。國(guó)產(chǎn)瀏覽器的話一般可以通過下載擴(kuò)展文件手動(dòng)拖動(dòng)的方式來安裝。下圖是微軟新版Edge瀏覽器的擴(kuò)展商店,直接搜索Tampermonkey即可。

也可以點(diǎn)擊這里下載油猴插件

新建腳本

也可以點(diǎn)擊這里獲取已經(jīng)寫好的油猴腳本

首先在瀏覽器右上角找到并點(diǎn)擊油猴插件,選擇添加新腳本。

然后就會(huì)打開如圖所示的編輯器窗口,我們就可以在其中編輯自己的腳本文件了。如果你喜歡的話,還可以將腳本內(nèi)容復(fù)制到合適的編輯器中編輯,完成之后再?gòu)?fù)制回來。

如果你點(diǎn)擊開發(fā)者菜單的話,可以選擇ES6模板,然后就可以在腳本中使用新版JavaScript的特性了,它會(huì)有Babel轉(zhuǎn)譯回ES5。不過這個(gè)模板貌似有點(diǎn)問題,用了它就沒辦法使用代碼糾錯(cuò)功能了。所以這里我還是選擇了默認(rèn)的ES5模板。

腳本編寫方法

功能注釋

首先來看看腳本的內(nèi)容,上面是一大排注釋,這些注釋可以非常有用的,它表明了腳本的各個(gè)屬性。下面來簡(jiǎn)單介紹一下。

屬性名 作用
name 油猴腳本的名字
namespace 命名空間,類似于Java的包名,用來區(qū)分相同名稱的腳本,一般寫成作者名字或者網(wǎng)址就可以了
version 腳本版本,油猴腳本的更新會(huì)讀取這個(gè)版本號(hào)
description 描述,用來告訴用戶這個(gè)腳本是干什么用的
author 作者名字
match 只有匹配的網(wǎng)址才會(huì)執(zhí)行對(duì)應(yīng)的腳本,例如 * 、 http://* 、 http://www.baidu.com/*等,參見 谷歌開發(fā)者文檔
grant 指定腳本運(yùn)行所需權(quán)限,如果腳本擁有相應(yīng)的權(quán)限,就可以調(diào)用油猴擴(kuò)展提供的API與瀏覽器進(jìn)行交互。如果設(shè)置為 none 的話,則不使用沙箱環(huán)境,腳本會(huì)直接運(yùn)行在網(wǎng)頁(yè)的環(huán)境中,這時(shí)候無(wú)法使用大部分油猴擴(kuò)展的API。如果不指定的話,油猴會(huì)默認(rèn)添加幾個(gè)最常用的API
require 如果腳本依賴其他js庫(kù)的話,可以使用require指令,在運(yùn)行腳本之前先加載其他庫(kù),常見用法是加載jquery
connect 當(dāng)用戶使用 GM_xmlhttpRequest 請(qǐng)求遠(yuǎn)程數(shù)據(jù)的時(shí)候,需要使用connect指定允許訪問的域名,支持域名、子域名、IP地址以及 * 通配符
updateURL 腳本更新網(wǎng)址,當(dāng)油猴擴(kuò)展檢查更新的時(shí)候,會(huì)嘗試從這個(gè)網(wǎng)址下載腳本,然后比對(duì)版本號(hào)確認(rèn)是否更新

腳本權(quán)限

下面簡(jiǎn)單介紹一下grant指令那里可以填寫的一些權(quán)限,詳情請(qǐng)查看 油猴腳本文檔 。這里就簡(jiǎn)單介紹幾個(gè)常用的,可以調(diào)用的函數(shù)全部以GM_作為開頭。

權(quán)限名 功能
unsafeWindow 允許腳本可以完整訪問原始頁(yè)面,包括原始頁(yè)面的腳本和變量。
GM_getValue(name,defaultValue) 從油猴擴(kuò)展的存儲(chǔ)中訪問數(shù)據(jù)??梢栽O(shè)置默認(rèn)值,在沒成功獲取到數(shù)據(jù)的時(shí)候當(dāng)做初始值。如果保存的是日期等類型的話,取出來的數(shù)據(jù)會(huì)變成文本,需要自己轉(zhuǎn)換一下。
GM_setValue(name,value) 將數(shù)據(jù)保存到存儲(chǔ)中
GM_xmlhttpRequest(details) 異步訪問網(wǎng)頁(yè)數(shù)據(jù)的API,這個(gè)方法比較復(fù)雜,有大量參數(shù)和回調(diào),詳情請(qǐng)參考官方文檔。
GM_setClipboard(data, info) 將數(shù)據(jù)復(fù)制到剪貼板中,第一個(gè)參數(shù)是要復(fù)制的數(shù)據(jù),第二個(gè)參數(shù)是MIME類型,用于指定復(fù)制的數(shù)據(jù)類型。
GM_log(message) 將日志打印到控制臺(tái)中,可以使用F12開發(fā)者工具查看。
GM_addStyle(css) 像網(wǎng)頁(yè)中添加自己的樣式表。
GM_notification(details, ondone), GM_notification(text, title, image, onclick) 設(shè)置網(wǎng)頁(yè)通知,請(qǐng)參考文檔獲取用法。
GM_openInTab(url, loadInBackground) 在瀏覽器中打開網(wǎng)頁(yè),可以設(shè)置是否在后臺(tái)打開等幾個(gè)選項(xiàng)

還有一些API沒有介紹,請(qǐng)大家直接查看官方文檔吧。

編寫腳本

編寫腳本就很簡(jiǎn)單了,編寫到 // Your code here .. 那里即可??梢跃帉懞瘮?shù),然后在最后調(diào)用這幾個(gè)函數(shù),這樣的模塊化編寫方法寫出來的腳本比較容易維護(hù)。

等vagrant更新時(shí)候提醒我的腳本

前段時(shí)間了解了vagrant這個(gè)東西,感覺很有意思,準(zhǔn)備研究一下,但是照著官網(wǎng)教程運(yùn)行的時(shí)候,第一步就發(fā)生了錯(cuò)誤。我上網(wǎng)一搜,原來我更新的virtualbox比較新,vagrant恰好不支持。但是如今幾個(gè)月過去了,vagrant還是沒有更新,所以我要寫一個(gè)腳本,等到vagrant更新的時(shí)候,給我網(wǎng)頁(yè)上彈出一個(gè)對(duì)話框。

首先訪問 vagrant官網(wǎng) ,然后就可以看到中間下載按鈕上大大的版本號(hào)2.2.6了。因?yàn)榘姹究隙ㄊ遣粫?huì)倒退的,所以只要判斷一下版本號(hào)不是2.2.6,就可以彈出提示了。通過F12開發(fā)者工具可以看到,這三個(gè)按鈕其實(shí)都是鏈接,只不過顯示成了按鈕的樣子,而且他們恰好都位于 header 標(biāo)簽之中。如果如果可以的話,直接用選擇器就可以非常輕松的獲取到版本號(hào)。

為了能在更新的時(shí)候及時(shí)獲取到提示,我需要腳本在所有網(wǎng)站上生效,來檢測(cè)版本。但是這樣做會(huì)導(dǎo)致另外一個(gè)問題,那就是每次打開一個(gè)網(wǎng)頁(yè)都會(huì)運(yùn)行一次檢查vagrant的腳本,而這是完全不必要的。所以需要一個(gè)額外的判斷,這就需要利用油猴提供的API來保存當(dāng)前日期,只有每天第一次的時(shí)候才會(huì)執(zhí)行檢查代碼。本來我想的很復(fù)雜,需要一個(gè)日期變量,然后還要額外一個(gè)變量保存是否是今天第一次更新。后來我發(fā)現(xiàn)我想的太多了,做法其實(shí)很簡(jiǎn)單。每天先獲取一次日期,然后和事先保存的日期比較,如果不一樣的話才執(zhí)行腳本,并將日期設(shè)置為今天的日期;如果日期一樣的話無(wú)事發(fā)生。

最后一個(gè)問題就是如何來判斷版本號(hào),有兩種方法:第一種就是上面提到的,直接解析HTML代碼并找到版本號(hào);第二種是更直接的辦法, 因?yàn)関agrant也是Github上開源的項(xiàng)目,所以可以直接調(diào)用Github的API來獲取最新發(fā)布的版本號(hào)??上У氖牵诙N辦法我試了一下居然不成功,不知為何,沒辦法獲取到發(fā)布信息,但是換成其他項(xiàng)目就可以。所以最后沒辦法只好采用第一種辦法。有興趣的同學(xué)可以自己試一下第二種方法。

好了,所有相關(guān)的坑我都已經(jīng)解釋完畢了,相信大家應(yīng)該很容易就可以看懂下面的代碼,我就不介紹了。雖然看著簡(jiǎn)單,但是我其實(shí)還是踩了不少的坑,就這點(diǎn)代碼花了我好幾天的時(shí)間。而且確實(shí)這個(gè)代碼寫的也并不是很好,因?yàn)閍jax取回來的代碼是完整一個(gè)html頁(yè)面,貌似用原版DOM API沒辦法解析,最后只好用jQuery的 parseHTML 方法解析的。而且我還因?yàn)樵椒ê蚸Query之間的方法名搞混了,浪費(fèi)了很多時(shí)間。

// ==UserScript==
// @name remind_me_vagrant_update
// @namespace https://github.com/techstay/myscripts
// @version 0.1
// @description remind me if vagrant support virtualbox
// @author techstay
// @match *
// @require https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @connect vagrantup.com
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_log
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// ==/UserScript==

(function () {
 'use strict';

 const CHECKED_DATE = 'checkedDate';

 function checkDateEquals(a, b) {
 return a.getFullYear() === b.getFullYear() 
 a.getMonth() === b.getMonth() 
 a.getDate() === b.getDate();
 }

 function checkVagrantVersion() {
 GM_setValue(CHECKED_DATE, new Date());
 GM_xmlhttpRequest({
 "method": "GET",
 "url": "https://www.vagrantup.com/",
 "headers": {
 "user-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
 },
 "onload": function (result) {
 var list = jQuery.parseHTML(result.response);
 jQuery.each(list, function (i, el) {
  if (el.nodeName == 'HEADER') {
  var header = jQuery.parseXML(el.innerHTML);
  var version = header.getElementsByTagName('a')[1].textContent.replace('Download ', '');
  if (version != '2.2.6') {
  alert('Vagrant update!');
  }
  return false;
  }
 });
 }
 });
 }

 var today = new Date();
 var lastCheckedDay = new Date(GM_getValue(CHECKED_DATE, new Date('2006-1-1')));
 if (!checkDateEquals(lastCheckedDay, today)) {
 checkVagrantVersion();
 }

})();

調(diào)試腳本

編寫腳本很難一次成功,大部分時(shí)間都花在了調(diào)試上面。調(diào)試油猴腳本的話有幾種調(diào)試方法。

第一種方法就是最原始的打印日志,可以利用 console.logGM_log 來將關(guān)鍵信息打印出來,上面的腳本就是我靠打印日志一點(diǎn)點(diǎn)發(fā)現(xiàn)各種參數(shù)錯(cuò)誤的。說實(shí)話這種辦法有點(diǎn)笨。

第二種就是利用瀏覽器的調(diào)試功能,在腳本需要調(diào)試的地方插入 debugger; 語(yǔ)句,然后在打開F12開發(fā)者工具的情況下刷新頁(yè)面,就會(huì)發(fā)現(xiàn)網(wǎng)頁(yè)已經(jīng)暫停在相應(yīng)位置上。這樣就可以利用F12開發(fā)者工具進(jìn)行單步調(diào)試、監(jiān)視變量等操作了。

將文章同步復(fù)制到Csdn和思否編輯器的腳本

我的文章一般都是簡(jiǎn)書首發(fā),然后復(fù)制粘貼到Csdn中,但是后來我發(fā)現(xiàn)每次手動(dòng)操作太蠢了,為什么不用腳本來自動(dòng)化呢?所以我又寫了個(gè)腳本幫忙完成自動(dòng)化工作。本來以為這個(gè)腳本應(yīng)該比較簡(jiǎn)單,不過還是踩了很多坑才湊合把功能寫出來。

首先是數(shù)據(jù)的保存,利用油猴提供的 GM_setValue 倒是可以很簡(jiǎn)單的將文章標(biāo)題和內(nèi)容保存起來。不過問題來了,如何在不同頁(yè)面之間共享呢?有幾種方案:第一種最簡(jiǎn)單粗暴,直接復(fù)制兩份,對(duì)應(yīng)頁(yè)面首先判斷是否存在數(shù)據(jù),存在的話才執(zhí)行復(fù)制操作,然后清空數(shù)據(jù)。這種方案最簡(jiǎn)單,而且如果自己直接新建文章的話也不會(huì)出問題。第二種就是數(shù)據(jù)只保存一份,通過幾個(gè)變量來確定什么時(shí)候復(fù)制完成,清空數(shù)據(jù),但是這樣比較復(fù)雜,要理清邏輯順序很麻煩。所以最后我就采用了第一種辦法。

然后又遇到一個(gè)問題,那就是如果編輯器自帶了保存和恢復(fù)功能,很可能會(huì)把我復(fù)制過去的文章給覆蓋了,所以需要等頁(yè)面加載完之后,延遲一段時(shí)間才進(jìn)行復(fù)制操作。然后我又谷歌了一番,差不多解決了這個(gè)問題。

然后遇到了一個(gè)非常棘手的問題,就是SF的編輯器設(shè)計(jì)比較復(fù)雜,沒辦法通過直接填充 value 或者 text 屬性的方式來寫入文章,我想了很久也沒有想出來怎么解決。沒辦法只好改用剪貼板的方式來糊弄了,也就是將文章內(nèi)容復(fù)制到剪貼板里頭,然后手動(dòng)粘貼到編輯器中。

最后一個(gè)問題就是簡(jiǎn)書上這個(gè)復(fù)制按鈕應(yīng)該如何實(shí)現(xiàn),其實(shí)簡(jiǎn)書編輯器的工具欄倒是空了一些部分,我本來想把按鈕直接加到那個(gè)上面。但是我發(fā)現(xiàn)貌似一旦添加?xùn)|西,那個(gè)工具欄會(huì)自動(dòng)重載取消更改,所以水平所限沒做到,只好利用jQueryUI加了一個(gè)很丑的浮動(dòng)按鈕,而且因?yàn)橥蟿?dòng)的時(shí)候會(huì)觸發(fā)單擊,沒辦法把按鈕改成了雙擊觸發(fā)。

最后的腳本就是下面這樣的。相比第一個(gè)腳本多了幾個(gè)打開新頁(yè)面、刪除變量、訪問剪貼板的API。

// ==UserScript==
// @name copy_jianshu_to_csdn_and_segmentfault
// @namespace https://github.com/techstay/myscripts
// @version 0.1
// @description 將簡(jiǎn)書文章復(fù)制到csdn和思否編輯器中
// @author techstay
// @match https://editor.csdn.net/md/
// @match https://segmentfault.com/write
// @match https://www.jianshu.com/writer*
// @require https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @require https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant unsafeWindow
// @grant GM_setClipboard
// @grant window.close
// @grant window.focus
// @grant GM_openInTab
// ==/UserScript==
(function () {
 'use strict';

 const SF_URL = 'https://segmentfault.com/write'
 const CSDN_URL = 'https://editor.csdn.net/md/'

 const SF_TITLE = 'sf_title'
 const SF_CONTENT = 'sf_content'
 const CSDN_TITLE = 'csdn_title'
 const CSDN_CONTENT = 'csdn_content'

 function saveArticle() {
 GM_setValue(CSDN_TITLE, $('._24i7u').val())
 GM_setValue(CSDN_CONTENT, $('#arthur-editor').val())
 GM_setValue(SF_TITLE, $('._24i7u').val())
 GM_setValue(SF_CONTENT, $('#arthur-editor').val())
 }

 function copyToCsdn() {
 var title = GM_getValue(CSDN_TITLE, '')
 var content = GM_getValue(CSDN_CONTENT, '')
 if (title != ''  content != '') {
 $('.article-bar__title').delay(2000).queue(function () {
 $('.article-bar__title').val(title)
 $('.editor__inner').text(content)
 GM_deleteValue(CSDN_TITLE)
 GM_deleteValue(CSDN_CONTENT)
 $(this).dequeue()
 })
 }
 }

 function copyToSegmentFault() {
 $(document).ready(function () {
 var title = GM_getValue(SF_TITLE, '')
 var content = GM_getValue(SF_CONTENT, '')
 if (title != ''  content != '') {
 $('#title').delay(2000).queue(function () {
  $('#title').val(title)
  GM_setClipboard(content, 'text')
  GM_deleteValue(SF_TITLE)
  GM_deleteValue(SF_CONTENT)
  $(this).dequeue()
 })

 }
 })

 }

 function addCopyButton() {
 $('body').append('div id="copyToCS">雙擊復(fù)制到CSDN和思否/div>')
 $('#copyToCS').css('width', '200px')
 $('#copyToCS').css('position', 'absolute')
 $('#copyToCS').css('top', '70px')
 $('#copyToCS').css('left', '350px')
 $('#copyToCS').css('background-color', '#28a745')
 $('#copyToCS').css('color', 'white')
 $('#copyToCS').css('font-size', 'large')
 $('#copyToCS').css('z-index', 100)
 $('#copyToCS').css('border-radius', '25px')
 $('#copyToCS').css('text-align', 'center')
 $('#copyToCS').dblclick(function () {
 saveArticle()
 GM_openInTab(SF_URL, true)
 GM_openInTab(CSDN_URL, true)
 })
 $('#copyToCS').draggable()
 }

 $(document).ready(function () {
 if (window.location.href.startsWith('https://www.jianshu.com')) {
 addCopyButton()
 } else if (window.location.href.startsWith(SF_URL)) {
 copyToSegmentFault()
 } else if (window.location.href.startsWith(CSDN_URL)) {
 copyToCsdn()
 }
 })
})()

其他注意事項(xiàng) 腳本編寫流程

踩了幾天坑,最后總結(jié)一下編寫油猴腳本的一點(diǎn)步驟。首先要思考腳本的實(shí)現(xiàn)方式,需要用到什么API和權(quán)限,然后填寫好腳本的注釋信息。

然后將功能封裝成函數(shù)的形式,最后在腳本末尾調(diào)用實(shí)現(xiàn)的函數(shù)。寫的差不多的時(shí)候復(fù)制到瀏覽器中嘗試運(yùn)行。

遇到困難的時(shí)候,可能需要直接在F12開發(fā)者工具里進(jìn)行調(diào)試。有些網(wǎng)頁(yè)不用jQuery,為了方便,我們需要自己將jQuery導(dǎo)入到頁(yè)面中,可以將下面的代碼復(fù)制到瀏覽器控制臺(tái)中。

var jq = document.createElement('script');
jq.src = "https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

發(fā)布腳本 更新URL

腳本做完了,自然是要共享出來讓大家一起使用的。當(dāng)然既然要發(fā)布,自然要支持更新方便日后維護(hù)。方法也很簡(jiǎn)單,直接在上面的注釋部分添加 updateURL 即可,然后設(shè)置腳本訪問地址。例如我要將腳本發(fā)布到Github上,就添加下面的注釋。

// @updateURL https://raw.githubusercontent.com/techstay/myscripts/master/tampermonkey/remind_me_vagrant_update.js

上傳腳本

油猴腳本支持好幾個(gè)網(wǎng)站,其中目前最主流的是 GreasyFork ,登錄這個(gè)網(wǎng)站注冊(cè)一個(gè)賬號(hào),然后進(jìn)入用戶頁(yè)面選擇提交腳本,然后填寫腳本代碼和各項(xiàng)信息。

這樣腳本就提交上去了,其他人也可以搜索到并安裝腳本了!

總結(jié)

到此這篇關(guān)于油猴腳本編寫教程詳解的文章就介紹到這了,更多相關(guān)油猴腳本編寫教程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

標(biāo)簽:新疆 紅河 沈陽(yáng) 樂山 河南 上海 滄州 長(zhǎng)治

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《油猴腳本編寫教程詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266