油猴插件录制请求,封装接口自动化参数

参考:如何使用油猴插件提高测试工作效率

一、背景

在酷家乐设计工具测试中,总会有许多高频且较繁琐的工作,比如:

  • 查询插件版本:需要打开Chrome控制台,输入好几个命令然后过滤出版本信息。

  • 查询模型商品:需要先打开调试工具,查询得到模型商品id,然后跳转到测试平台进行加密,再去商家后台拼接url,最终访问到商品详情页。

  • 修改定制高级配置:至少要点击4次页面跳转,才能开始配置。

类似的重复性工作实在太多,无形中影响工作效率与体验。并且大量的命令记忆对新手特别不友好。

仔细分析这类行为,大多都属于"数据查询"、“命令输入” 、“页面访问” 等简单操作的组合,其实非常适合“插件化”,封装成各种【一键操作】。

二、思路

基于上述背景,我们期望能开发一个插件来提高测试工作效率。

对于测试插件,主要有以下诉求:

  • 开发门槛低。能让更多人参与进来,实现丰富的功能,满足各种需求。

  • API 强大。便于扩展更多能力。

  • 插件更新方便。便于新功能的推广。

最容易想到有两种方案: 酷家乐工具内部集成插件、Chrome 插件。但是很明显,这两种方式都存在开发门槛高、维护成本高、使用场景有限的缺点。

所以最后选择了另一种方案---油猴插件

什么是油猴插件?

篡改猴 (Tampermonkey) 是拥有 超过 1000 万用户 的最流行的浏览器扩展之一。它适用于 ChromeMicrosoft EdgeSafari 等主流浏览器。
它允许用户自定义并增强您最喜爱的网页的功能。用户脚本是小型 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();
})();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/418452.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++的封装

实现my_string中可能实现的功能 #include <iostream> #include<cstring> using namespace std; class my_string {char *str; //记录c风格的字符串int size; //记录字符串的实际长度int capacit;//记录最大容量public://无参的构造函数my_string():size(0)…

【人工智能学习笔记】1_人工智能基础

本系列是个人学习《阿里云人工智能工程师ACA认证免费课程&#xff08;2023版&#xff09;》的笔记&#xff0c;仅为个人学习记录&#xff0c;欢迎交流&#xff0c;感谢批评指正 人工智能概述 智能的三大能力&#xff1a;感知、记忆与思维、学习与适应能力人工智能的定义 明斯基…

AI文献综述神器,有这一款就够了!

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 当前的AI辅助文献综述的工具有很多&#xff0c;如果说功能最强大的&#xff0c;娜姐无疑要推SciSpace了。 SciSpace利用强大的AI算法&#xff0c;理解并建立研究论文之间的联…

图特征工程实践指南:从节点中心性到全局拓扑的多尺度特征提取

图结构在多个领域中扮演着重要角色&#xff0c;它能有效地模拟实体间的连接关系&#xff0c;通过从图中提取有意义的特征&#xff0c;可以获得宝贵的信息提升机器学习算法的性能。 本文将介绍如何利用NetworkX在不同层面&#xff08;节点、边和整体图&#xff09;提取重要的图…

FPGA开发:Verilog数字设计基础

EDA技术 EDA指Electronic Design Automation&#xff0c;翻译为&#xff1a;电子设计自动化&#xff0c;最早发源于美国的影像技术&#xff0c;主要应用于集成电路设计、FPGA应用、IC设计制造、PCB设计上面。 而EDA技术就是指以计算机为工具&#xff0c;设计者在EDA软件平台上…

华为 HCIP-Datacom H12-821 题库 (4)

有需要题库的可以看主页置顶 V群仅进行学习交流 1.缺省情况下&#xff0c;广播型网络中运行 IS-IS 的路由器&#xff0c;DIS 发送 CSNP报文的周期为多少秒&#xff1f; A、10 B、3.3 C、30 D、40 答案&#xff1a;A 解析&#xff1a; 广播型网络中运行 IS-IS 的路由器&am…

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)

ubuntu 20.04 一直卡在登录界面&#xff0c;即使密码正确也无法登录 这次是装实体机&#xff0c;一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional …

ansible+awx搭建

1、环境介绍 操作系统版本&#xff1a;龙蜥os 8.9 docker 版本&#xff1a;26.0.2 python版本&#xff1a;3.11.9 ansible版本&#xff1a;2.16.6 awx版本&#xff1a;24.2.0 2、安装docker 设置软件源 yum install -y yum-utilsyum-config-manager \--add-repo \https://mirr…

9. GIS技术支持工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

Docker数据卷和Dockerfile

1、什么是Docker数据卷 前言&#xff1a; 在下载的镜像中&#xff0c;我们不能够去改变它内部的一些配置&#xff0c;因为docker的镜像文件是已经配置好的&#xff0c;无法改变&#xff0c;我们只能改变镜像启动后的容器里面的内容&#xff0c;但是又因为&#xff0c;容器本来…

Windows .NET8 实现 远程一键部署,几秒完成发布,提高效率 - CICD

1. 前言 场景 &#xff08;工作环境 一键部署 到 远端服务器 [阿里云]&#xff09; CICD 基本步骤回顾 https://blog.csdn.net/CsethCRM/article/details/141604638 2. 环境准备 服务器端IP&#xff1a;106.15.74.25&#xff08;阿里云服务器&#xff09; 客户端&#xff1…

STM32学习笔记4 --- USART

目录 通信接口1 USART 串口的通信协议 硬件部分&#xff1a; 软件部分&#xff1a; 字节数据的传递&#xff1a; stm32内部的USART外设 串口发送 串口发送接收 Hex数据包 文本数据包 数据包的收发流程 串口收发Hex数据包 串口收发文本数据包 通信接口1 USART US…

Java+Selenium+ChromeDriver谷歌版环境搭建

1、创建测试项目 创建一个Maven项目即可 2、添加Selenium依赖 最好使用Selenium3版本 3、下载对应版本的ChromeDriver 找到自己浏览器对应的版本 下载ChromeDriver&#xff08;114版本以后的&#xff0c;114版之前的直接到官网下载&#xff09;下载地址 将下载好的驱动…

Nacos注册中心与OpenFeign远程调用

文章目录 一、注册中心原理二、Nacos注册中心三、服务注册四、服务发现五、OpenFeign 一、注册中心原理 在微服务当中必须有两个角色 服务提供者&#xff1a;提供接口供其它微服务访问 服务消费者&#xff1a;调用其它微服务提供的接口 在大型微服务项目中&#xff0c;服务提供…

探索EasyCVR与AI技术深度融合:视频汇聚平台的新增长点

随着5G、AI、边缘计算、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联已经从概念逐渐转变为现实&#xff0c;AIoT&#xff08;物联网人工智能&#xff09;的新时代正在加速到来。在这一背景下&#xff0c;视频技术作为信息传输和交互的重要手…

【免费分享】GIS开发面试题(流程+自我介绍+基础篇+Openlayermapbox)

本篇文章针对GIS应届生就业方向及面试困惑问题进行了收集整理&#xff0c;并列出了关于GIS开发面试中常见的问题&#xff08;含答案&#xff09;。 “ 包括以下内容 前言 简介 面试之前 面试流程 自我介绍-AI 基础篇 1、GIS八股文基础篇 2、Openlayers图形绘制 3、倾…

注解实现json序列化的时候自动进行数据脱敏

最近在进行开发的时候遇到一个问题&#xff0c;需要对用户信息进行脱敏处理&#xff0c;原有的方式是写一个util类&#xff0c;在需要脱敏的字段查出数据后&#xff0c;显示掉用方法处理后再set回去&#xff0c;觉得这种方式能实现功能&#xff0c;但是不是特别优雅&#xff0c…

PostgreSQL + PostGIS:空间数据存储及管理解决方案

在数据库领域&#xff0c;PostgreSQL 已成为最强大、最通用的选项之一。它管理大量数据的能力、对 SQL 标准的遵守以及可扩展的架构使其受到学术界和工业界的喜爱。然而&#xff0c;真正让 PostgreSQL 脱颖而出的原因之一是它与PostGIS的集成&#xff0c;这是一个允许您有效处理…

HTML 基础,尚优选网站设计开发(二)

最近在恶补HTML相关知识点&#xff0c;本人是后端程序员&#xff0c;看到周围很多人都被裁员了&#xff0c;突然想尽早转变成全栈程序员变成独立开发者&#xff0c;有空余接接私单、商单的 尚优选网站设计开发&#xff0c;HTMLCSSJavaScript实际使用 尚优选网站设计开发页面分析…

单元测试 Mock不Mock?

文章目录 前言单元测试没必要?Mock不Mock?什么是Mock?Mock的意义何在? 如何Mock&#xff1f;应该Mock什么&#xff1f;Mock 编写示例 总结 前言 前段时间&#xff0c;我们团队就单元测试是否采用 Mock 进行了一番交流&#xff0c;各有各的说法。本文就单元测试 Mock不Mock…