希望用油猴做一些自动化的工具. 不用自动化测试软件的原因是特征都能被浏览器和页面检测出来, 所以这就存在一些风险. UiBot RPA虽然也可以, 没有检测痕迹, 但一是感觉软件太重了, 二是只能解决一些简单的Web上的问题, 很多时候复杂的问题如shadowRoot还需要配合js代码注入, 三是Uibot的代码编辑页实在有点难用,仿VB语言的算法也实在不太适应,尤其是不等号<>
, 还有赋值和比较都是=
不区分, 四是缺乏第三方库.
先推荐一个教程, 据说是中文唯一写的好的. https://github.com/scriptscat/tampermonkey-learn-guide
油猴官方可以说没有教程tutorial, 只有按字母序排列的API的说明. 虽然会JS上手也能写, 但只有明白额外的API才能进阶高级, 感觉有点像是electron, 不知道油猴是不是能实现类似electron的效果?
油猴名字
油猴这个翻译其实是grease monkey, 代码里简称的GM就是. 而tamper monkey是破坏猴的意思.
// @
最前面的注释部分, 以// @
开头的其实都是给油猴看的代码, 类似于预定义, 是和油猴交互的关键. 其中最重要的一定要改的部分呢列出来.
//@命令 |
子命令 |
解释 |
//@name |
名称 |
脚本名称 |
//@match |
网址 |
匹配网址, 也就是在哪个网址运行. 支持通配符* |
// @grant |
none |
脚本直接注入window环境, 同时也不能使用GM_*命令了,所以一般不用 |
- |
unsafeWindow |
直接使用页面中的任何脚本. 这就会打通页面和本地,理论上页面可以窃取本地内容并操控本地的一切. 脚本中的window需要替换为unsafeWindow |
- |
GM_* |
申请指定的GM函数权限.页面window环境需要用unsafeWindow访问. |
- |
GM_xmlhttpRequest |
允许跨域请求API |
- |
GM_setValue/ GM_getValue/ GM_deleteValue/ GM_addValueChangeListener/ GM_removeValueChangeListener/ GM_listValues |
设置/获取/删除/监听/停止监听/列出本地键值对 |
- |
GM_registerMenuCommand /GM_unregisterMenuCommand |
在油猴按钮上注册菜单 |
- |
GM_addStyle |
增加 |
- |
window.onurlchange/.close/.focus |
这三个window权限需要grant声明才能用 |
// @run-at |
document-start |
在document加载时就尽快调用脚本(比页面脚本更早) |
context-menu |
右键增加tempermonkey 菜单,子菜单为本脚本名称, 点击运行 |
// @connect |
网址 |
允许被GM_xmlhttpRequest请求的网址 |
// @require |
js文件URL#md5=xxxx |
加载外部js资源文件并附带md5校验.引入油猴环境还是页面环境要看@grant定义. 也可以通过在html中插入script的方式来引用 |
// @resource |
css css文件URL |
加载外部css |
常用js代码部分
在html中增加元素
let div = document.createElement("div");
div.innerHTML = '<span>span1</span><span>span2</span>';
// 插入到页面的body中
document.body.append(div);
在html中增加按钮并监听
监听单个元素
let btn = document.createElement("button");
btn.innerHTML =
"按钮文字,其实也可以写html,变成下面的样子(不过谁用按钮来包那么多html标签呢)";
//innerText也可以,区别是innerText不会解析html
btn.onclick = function () {
alert("点击了按钮");
};
document.body.append(btn);
监听多个元素
let div = document.createElement("div");
div.innerHTML =
'<span id="span-1">span1</span><span class="sp">span class</span>';
div.onclick = function (event) {
if (event.target.id == "span-1") {
alert("span-1被点击了");
} else if (event.target.className == "sp") {
alert("sp这一类被点了");
}
};
document.body.append(div);
用事件监听器去减轻监听
div.addEventListener("click", function (ev) {
console.log(ev);
});
在html中插入script
let script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.src = "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js";
document.documentElement.appendChild(script);
在html中插入css
let script = document.createElement('link');
script.setAttribute('rel', 'stylesheet');
script.setAttribute('type', 'text/css');
script.href = "https://blog.icodef.com/wp-content/themes/Kratos-3.0.7/assets/css/kratos.min.css?ver=3.2.4";
document.documentElement.appendChild(script);
直接用GM_addStyle插入样式
GM_addStyle("#HMRichBox{display:none !important}");
在页面加载完毕后再执行
等所有元素加载完毕(包括图像等)
window.onload = ()=>{
alert('loaded')
}
在油猴按钮上注册菜单 GM_registerMenuCommand
let id = GM_registerMenuCommand(
"自定义的菜单",
function () {
alert("菜单被点击");
GM_unregisterMenuCommand(id); //删除菜单
},
"h" // 这个是accessKey, 快捷键
);
其他问题
如何对付嵌套iframe的页面
要看iframe打开的是哪个url, @match进去就好
如何劫持js页面函数
在window下面的函数都可以直接劫持. 如 window.setInterval = 自己的函数
, 页面中自定义的函数也可以这么劫持, 如window.页面函数 = 自己的函数
几个常常劫持的函数, 除了setInterval
函数劫持后可以改变时间帧间隔外, XMLHttpRequest
函数劫持可以获取网络请求. 比如劫持XMLHttpRequest.prototype.send
就可以修改发送的内容.
除了用XMLHttpRequest
,页面还可能用fetch获取服务, 这时候需要对fetch劫持
let oldFetch = fetch;
function hookFetch(url, init) {
//这里进行提交内容劫持
return oldFetch.apply(this, arguments);
}
window.fetch = hookFetch;
对事件监听器劫持
const oldEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (...args) {
console.log("addEventListener Hook", this, ...args);
return oldEventListener.call(this, ...args);
};
其他可以劫持的还包括window.WebSocket
还可以劫持原型链上的函数. 如某个object.func()在其原型链上, 可以object.prototype.func = 自己的函数
如何在代码中打断点
在代码中使用debugger;
命令,F12打开时就会停在这儿