油猴插件录制请求,封装接口自动化参数
参考:如何使用油猴插件提高测试工作效率
一、背景
在酷家乐设计工具测试中,总会有许多高频且较繁琐的工作,比如:
-
查询插件版本:需要打开Chrome控制台,输入好几个命令然后过滤出版本信息。
-
查询模型商品:需要先打开调试工具,查询得到模型商品id,然后跳转到测试平台进行加密,再去商家后台拼接url,最终访问到商品详情页。
-
修改定制高级配置:至少要点击4次页面跳转,才能开始配置。
类似的重复性工作实在太多,无形中影响工作效率与体验。并且大量的命令记忆对新手特别不友好。
仔细分析这类行为,大多都属于"数据查询"、“命令输入” 、“页面访问” 等简单操作的组合,其实非常适合“插件化”,封装成各种【一键操作】。
二、思路
基于上述背景,我们期望能开发一个插件来提高测试工作效率。
对于测试插件,主要有以下诉求:
-
开发门槛低。能让更多人参与进来,实现丰富的功能,满足各种需求。
-
API 强大。便于扩展更多能力。
-
插件更新方便。便于新功能的推广。
最容易想到有两种方案: 酷家乐工具内部集成插件、Chrome 插件。但是很明显,这两种方式都存在开发门槛高、维护成本高、使用场景有限的缺点。
所以最后选择了另一种方案---油猴插件。
什么是油猴插件?
篡改猴 (Tampermonkey) 是拥有 超过 1000 万用户 的最流行的浏览器扩展之一。它适用于 Chrome、Microsoft Edge、Safari 等主流浏览器。
它允许用户自定义并增强您最喜爱的网页的功能。用户脚本是小型 JavaScript 程序,可用于向网页添加新功能或修改现有功能。使用 篡改猴,您可以轻松在任何网站上创建、管理和运行这些用户脚本。简单说,油猴插件是一个 Chrome 插件,但是它的功能是一个脚本管理器,能将自定义的脚本注入到当前页面,让你的代码成为网页的一部分。
油猴提供的API:
Documentation | Tampermonkey
GM_*API 按功能主要分为 WEB请求类:GM_xmlhttpRequest(details)GM_webRequest(rules, listener)cookie操作:GM_cookie.list(details[, callback])GM_cookie.set(details[, callback])GM_cookie.delete(details, callback) tab选项卡操作:GM_getTab(callback) GM_saveTab(tab) GM_getTabs(callback) 键值对操作: GM_setValue(key, value) GM_getValue(key, defaultValue) GM_deleteValue(key) GM_listValues() GM_addValueChangeListener(key, (key, old_value, new_value, remote) => void) GM_removeValueChangeListener(listenerId) 修改dom: GM_addElement(tag_name, attributes), GM_addElement(parent_node, tag_name, attributes) 添加样式: GM_addStyle(css) 下载: GM_download(details), GM_download(url, name) 获取@resource 引入的资源文件的文本内容(比如js) GM_getResourceText(name) 获取@resource 引入的资源文件的源地址 GM_getResourceURL(name) 控制台打印GM_log(message)屏幕通知GM_notification(details, ondone),GM_notification(text, title, image, onclick)打开新选项卡GM_openInTab(url, options),GM_openInTab(url, loadInBackground)菜单注册 GM_registerMenuCommand(name, callback, accessKey) 菜单注销 GM_unregisterMenuCommand(menuCmdId) 设置剪切板 GM_setClipboard(data, info) windows窗体操作: 窗口地址改变 window.onurlchange 窗口关闭 window.close() 窗口聚焦 window.focus()
油猴脚本开发详解+油猴爬虫脚本实例_其它综合_脚本之家
demo1:页面上增加刷新按钮,且可以实现拖拽:
// ==UserScript==
// @name 测试插件
// @version 0.0.1
// @description 百度首页刷新
// @namespace baidu.com
// @match *://*/*
// @grant GM_addStyle
// ==/UserScript==const addContainerDiv=()=>{const containerDiv=document.createElement("div");containerDiv.id="test-tool"containerDiv.innerHTML= "<button>刷新1</button>"GM_addStyle('#test-tool {position:fixed;right:300px;top:280px;}')//containerDiv.addEventListener("click",()=>{window.location.reload()})document.body.appendChild(containerDiv);//设置可拖拽const dragButton=document.getElementById("test-tool");dragButton.onmousedown = function(ev){// 获取鼠标相对于盒子的坐标var x2 = ev.offsetX;var y2 = ev.offsetY;// 鼠标移动document.onmousemove = function (ev) {var x3 = ev.pageX;var y3 = ev.pageY;dragButton.style.top = y3 - y2 + "px";dragButton.style.left = x3 - x2 + "px"}}// 4.鼠标松开事件dragButton.onmouseup = function () {document.onmousemove = null;}
}(function() {'use strict';addContainerDiv()
})();
效果:(注意:如果加上刷新动作的话,会导致拖拽无效;所以先把这行代码注释掉了)
demo2:录制接口
可以看到接口一般有两种类型,分别是fetch和xhr
Fetch和XHR都是用于发起HTTP请求的技术,但它们有以下几点区别:
1
原生API vs ES6新增函数:XHR是浏览器提供的原生API,而Fetch是ES6中新增的全局函数。
2
使用对象差异:XHR使用XMLHttpRequest对象,而Fetch使用Promise对象。
3
Cookies默认携带:Fetch默认不会携带cookies,需要手动设置credentials属性;而XHR请求会自动携带cookies。
4
请求取消能力:XHR可以取消一个正在进行的请求,而Fetch目前没有原生的请求取消机制。
5
响应类型处理:XHR的responseType属性可以设置响应类型(text、json、blob等),而Fetch需要手动解析响应。
6
进度监听功能:XHR可以监听上传和下载的进度,而Fetch不支持此功能。
7
错误处理方式:在错误处理方面,Fetch只会在网络错误时reject Promise,其他错误都会被视为成功的响应,需要手动判断;而XHR则会在出现错误时reject Promise。
8
兼容性:XHR兼容性更好,在一些旧版本的浏览器中可能无法使用Fetch2。
9
关注分离:Fetch是一种关注分离的技术,把复杂的事情拆分成几个简单的步骤实现,并得到结果3。
10
底层抽象:Fetch API更底层,包括Request、Response、Headers、Body等原生对象,而XHR需要使用一个实例来发出请求和处理响应。
11
灵活性:Fetch API比XHR更灵活,可以明确的配置请求和响应4。
12
兼容性:XHR兼容性更好,在一些旧版本的浏览器中可能无法使用Fetch2。
综上所述,Fetch和XHR各有优缺点,开发者应当根据项目需求和兼容性要求选择合适的请求技术
针对xhr:
- 把运行时间设置为document-start,确保能拦截到较早发出的请求。
- 使用
@grant unsafeWindow
声明,授予脚本访问或修改全局窗口对象的权限。参考:油猴脚本高级应用:拦截与修改网页Fetch请求实战指南_油猴拦截请求-CSDN博客
这里有一点点无用代码,自己改改
// ==UserScript==
// @name 测试插件
// @run-at document-start
// @version 0.0.1
// @description 百度首页刷新
// @namespace baidu.com
// @match *://*/*
// @grant GM_addStyle
// @require http://code.jquery.com/jquery-1.11.0.min.js
// @grant unsafeWindow
// ==/UserScript==const addContainerDiv=()=>{const containerDiv=document.createElement("div");containerDiv.id="test-tool"containerDiv.innerHTML= "<button>刷新1</button>"GM_addStyle('#test-tool {position:fixed;right:300px;top:280px;}')var aweme_list=[];containerDiv.addEventListener("click",()=>{console.log('aba:');console.log("aweme_list"+aweme_list);// 定义包含名称和链接的数组const files = [];aweme_list.forEach((item)=>{if(item.aweme_type==0||item.awemeType==0||item.aweme_type==61||item.awemeType==61){try{files.push({name:item.desc,url:item.video.play_addr.url_list[0]})}catch{files.push({name:item.desc,url:item.video.playAddr[0]})}}if(item.aweme_type==68||item.awemeType==68){var urlList=[]item.images.forEach(img=>{try{urlList.push(img.url_list[0])}catch{urlList.push(img.urlList[0])}})files.push({name:item.desc,urlList:urlList})}});console.log(files);})document.body.appendChild(containerDiv);//设置可拖拽const dragButton=document.getElementById("test-tool");dragButton.onmousedown = function(ev){// 获取鼠标相对于盒子的坐标var x2 = ev.offsetX;var y2 = ev.offsetY;// 鼠标移动document.onmousemove = function (ev) {var x3 = ev.pageX;var y3 = ev.pageY;dragButton.style.top = y3 - y2 + "px";dragButton.style.left = x3 - x2 + "px"}}// 4.鼠标松开事件dragButton.onmouseup = function () {document.onmousemove = null;}
}(function() {'use strict';addContainerDiv();$(() => {function addXMLRequestCallback(callback) {// 是一个劫持的函数var oldSend, i;if (XMLHttpRequest.callbacks) {// 判断XMLHttpRequest对象下是否存在回调列表,存在就push一个回调的函数// we've already overridden send() so just add the callbackXMLHttpRequest.callbacks.push(callback);} else {// create a callback queueXMLHttpRequest.callbacks = [callback];// 如果不存在则在xmlhttprequest函数下创建一个回调列表// store the native send()oldSend = XMLHttpRequest.prototype.send;// 获取旧xml的send函数,并对其进行劫持// override the native send()XMLHttpRequest.prototype.send = function () {// process the callback queue// the xhr instance is passed into each callback but seems pretty useless// you can't tell what its destination is or call abort() without an error// so only really good for logging that a request has happened// I could be wrong, I hope so...// EDIT: I suppose you could override the onreadystatechange handler thoughfor (i = 0; i < XMLHttpRequest.callbacks.length; i++) {XMLHttpRequest.callbacks[i](this);}// 循环回调xml内的回调函数// 由于我们获取了send函数的引用,并且复写了send函数,这样我们在调用原send的函数的时候,需要对其传入引用,而arguments是传入的参数// call the native send()oldSend.apply(this, arguments);}}}// e.g.addXMLRequestCallback(function (xhr) {// 调用劫持函数,填入一个function的回调函数// 回调函数监听了对xhr调用了监听load状态,并且在触发的时候再次调用一个function,进行一些数据的劫持以及修改xhr.addEventListener("load", function () {if (xhr.readyState == 4 && xhr.status == 200) {// 获取URLvar url = new URL(xhr.responseURL);console.log("xhr接口:" + url);}});});})})();
另一种写法,可以拿到url+参数:
var originalSend = XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send = function(body) {var xhr = this;// 保存原始的onreadystatechange事件处理器var originalOnReadyStateChange = xhr.onreadystatechange;// 重写onreadystatechange事件处理器xhr.onreadystatechange = function() {if (xhr.readyState === 4) { // 请求已完成// 打印URL和请求参数console.log('Request URL:', xhr.responseURL);console.log('Request Parameters:', body);}// 如果存在,则调用原始的onreadystatechange事件处理器if (originalOnReadyStateChange) {originalOnReadyStateChange.apply(this, arguments);}};// 调用原始的send方法originalSend.apply(this, arguments);};
fetch的录制下来:
const originFetch = fetch;
unsafeWindow.fetch = (...arg) => {console.log('fetch arg', ...arg);//console.log('通过')return originFetch(...arg);}
如果想要修改响应的数据:
参考:https://zhuanlan.zhihu.com/p/436757974
let oldfetch = fetch; function fuckfetch() {return new Promise((resolve, reject) => {oldfetch.apply(this, arguments).then(response => {const oldJson = response.json;response.json = function() {return new Promise((resolve, reject) => {oldJson.apply(this, arguments).then(result => {result.hook = 'success';resolve(result);});});};resolve(response);});}); } window.fetch = fuckfetch;
完整demo要求:抓捕当前页面上的所有请求,得到fetch格式:
得到这一串代码:
fetch("http://xx/compare", {"headers": {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q=0.9","content-type": "application/json","proxy-connection": "keep-alive","token": “”,"referrerPolicy": "strict-origin-when-cross-origin","body": "{\"id\":\"3862\",“\Version\":\"001420240626\"}","method": "POST",
});
然后可以拷贝url和body,之后可以直接粘贴到接口自动化脚本中,方便编写脚本
// ==UserScript==
// @name 测试插件
// @run-at document-start
// @version 0.0.1
// @description 百度首页刷新
// @namespace baidu.com
// @match *://*/*
// @grant GM_addStyle
// @require http://code.jquery.com/jquery-1.11.0.min.js
// @grant unsafeWindow
// ==/UserScript==(function () {//'use strict';var apiList = [];var originalSend = XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send = function (body) {var xhr = this;// 保存原始的onreadystatechange事件处理器var originalOnReadyStateChange = xhr.onreadystatechange;// 重写onreadystatechange事件处理器xhr.onreadystatechange = function () {if (xhr.readyState === 4) { // 请求已完成apiList.push({url: xhr.responseURL,status: xhr.status,body: body})console.log("apiList", apiList);}// 如果存在,则调用原始的onreadystatechange事件处理器if (originalOnReadyStateChange) {originalOnReadyStateChange.apply(this, arguments);}};// 调用原始的send方法originalSend.apply(this, arguments);};const originFetch = fetch;unsafeWindow.fetch = (...arg) => {console.log('fetch arg', ...arg);//console.log('通过')return originFetch(...arg);}//控件function addContainerDiv() {const containerDiv = document.createElement("div");containerDiv.id = "test-tool"containerDiv.innerHTML = "<button>获取接口</button>"GM_addStyle('#test-tool {position:fixed;right:300px;top:280px;}')containerDiv.addEventListener("click", () => {//console.log(apiList);})document.body.appendChild(containerDiv);//设置可拖拽const dragButton = document.getElementById("test-tool");dragButton.onmousedown = function (ev) {// 获取鼠标相对于盒子的坐标var x2 = ev.offsetX;var y2 = ev.offsetY;// 鼠标移动document.onmousemove = function (ev) {var x3 = ev.pageX;var y3 = ev.pageY;dragButton.style.top = y3 - y2 + "px";dragButton.style.left = x3 - x2 + "px"}}// 4.鼠标松开事件dragButton.onmouseup = function () {document.onmousemove = null;}}addContainerDiv();
})();
相关文章:

油猴插件录制请求,封装接口自动化参数
参考:如何使用油猴插件提高测试工作效率 一、背景 在酷家乐设计工具测试中,总会有许多高频且较繁琐的工作,比如: 查询插件版本:需要打开Chrome控制台,输入好几个命令然后过滤出版本信息。 查询模型商品&…...

循环购模式!结合引流和复购于一体的商业模型!
欢迎各位朋友,我是你们的电商策略顾问吴军。今天,我将向大家介绍一种新颖的商业模式——循环购模式,它将如何改变我们的消费和收益方式。你是否好奇,为何商家会提供如此慷慨的优惠?消费一千元,不仅能够得到…...

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧
Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用&…...

c中 int 和 unsigned int
c语言中,char、short、int、int64以及unsigned char、unsigned short、unsigned int、unsigned int64等等类型都可以表示整数。但是他们表示整数的位数不同,比如:char/unisigned char表示8位整数; short/unsigned short表示16位整…...

sheng的学习笔记-AI-话题模型(topic model),LDA模型,Unigram Model,pLSA Model
AI目录:sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是话题模型(topic model) 话题模型(topic model)是一族生成式有向图模型,主要用于处理离散型的数据(如文本集合),在信息检索、自然语言处理等领域有广泛应用…...

html 页面引入 vue 组件之 http-vue-loader.js
一、http-vue-loader.js http-vue-loader.js 是一个 Vue 单文件组件加载器,可以让我们在传统的 HTML 页面中使用 Vue 单文件组件,而不必依赖 Node.js 等其他构建工具。它内置了 Vue.js 和样式加载器,并能自动解析 Vue 单文件组件中的所有内容…...

html+css网页设计 旅行 蜘蛛旅行社3个页面
htmlcss网页设计 旅行 蜘蛛旅行社3个页面 网页作品代码简单,可使用任意HTML辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1&#…...

考拉悠然产品发布会丨以悠然远智全模态AI应用平台探索AI行业应用
9月6日,成都市大模型新技术新成果发布暨供需对接系列活动——考拉悠然专场,在成都市高新区菁蓉汇盛大举行。考拉悠然重磅发布了悠然远智丨全模态AI应用平台,并精彩展示了交通大模型应用——智析快处等最新的AI产品和技术成果。 在四川省科学…...

LLM大模型学习:揭秘LLM应用构建:探究文本加载器的必要性及在LangChain中的运用
构建 LLM 应用为什么需要文本加载器,langchain 中如何使用文本加载器? 在不同的应用场景中需要使用不同的文本内容作为内容的载体,针对不同的类型的文本,langchain 提供了多种文本加载器来帮助我们快速的将文本切片,从…...
Flutter函数
在Dart中,函数为 一等公民,可以作为参数对象传递,也可以作为返回值返回。 函数定义 // 返回值 (可以不写返回值,但建议写)、函数名、参数列表 showMessage(String message) {//函数体print(message); }void showMessage(String m…...

P3565 [POI2014] HOT-Hotels
~~~~~ P3565 [POI2014] HOT-Hotels ~~~~~ 总题单链接 思路 ~~~~~ 设 g [ u ] [ i ] g[u][i] g[u][i] 表示在 u u u 的子树内,距离 u u u 为 i i i 的点的个数。 ~~~~~ 设 d p [ u ] [ i ] dp[u][i] dp[u][i] 表示: u u u 的子树内存在两个点 x , …...
设计模式 | 单例模式
定义 单例设计模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这种模式常用于需要控制对某些资源的访问的场景,例如数据库连接、日志记录等。 单例模式涉…...
Web安全之CSRF攻击详解与防护
在互联网应用中,安全性问题是开发者必须时刻关注的核心内容之一。跨站请求伪造(Cross-Site Request Forgery, CSRF),是一种常见的Web安全漏洞。通过CSRF攻击,黑客可以冒用受害者的身份,发送恶意请求&#x…...

IDEA运行Java程序提示“java: 警告: 源发行版 11 需要目标发行版 11”
遇到这个提示一般是在pom.xml中已经指定了构建的Java版本环境是11例如(此时添加了build插件的情况下虽然不能直接运行代码但是maven是可以正常打包构建): <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><…...
车载测试| 汽车的五域架构 (含线控技术知识)
汽车的五域架构是一种将汽车电子控制系统按照功能进行划分的架构模式,主要包括动力域、底盘域、座舱域、自动驾驶域和车身域。(汽车三域架构通常是指将汽车电子系统划分为三个主要领域:动力域、底盘域和智能座舱域(或车身舒适域&a…...

【Linux】gcc/g++ 、make/Makefile、git、gdb 的使用
目录 1. Linux编译器-gcc/g1.1 编译器gcc/g的工作步骤1.2 函数库1.2.1 函数库的作用及分类1.2.2 动态链接和静态链接1.2.3 动态库和静态库的优缺点 1.3 gcc选项 2. Linux项目自动化构建工具-make/Makefile2.1 .PHONY2.2 尝试编写进度条程序 3. git3.1 安装 git3.2 下载项目到本…...

Elastic Stack--ES的DSL语句查询
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 学习B站博主教程笔记: 最新版适合自学的ElasticStack全套视频(Elk零基础入门到精通教程)Linux运维必备—Elastic…...

ARM基础知识---CPU---处理器
目录 一、ARM架构 1.1.RAM---随机存储器 1.2.ROM---只读存储器 1.3.flash---闪存存储器 1.4.时钟(振晶) 1.5.复位 二、CPU---ARM920T 2.1.R0~R12---通用寄存器 2.2.PC程序计数器 2.3.LR连接寄存器 2.4.SP栈指针寄存器 2.5.CPSR当前程序状态寄存…...

将星 x17 安装ubuntu 20.04 双系统
准备工作,包含关闭快速启动,关闭Secret Boot 1.进入控制面板选择小图标,找到电源选项 2.点击更改当前不可用的设置,关闭快速启动 3.开机启动时快速按F2,进入BIOS 4.选择Setup Utiltity,选择Security&#…...

E31.【C语言】练习:指针运算习题集(上)
Exercise 1 求下列代码的运行结果 #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d",*(ptr - 1));return 0; } 答案速查: 分析: Exercise 2 求下列代码的运行结果 //在x86环境下 //假设结…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...