Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >油猴脚本重写fetch和xhr请求

油猴脚本重写fetch和xhr请求

作者头像
windliang
发布于 2022-09-27 00:36:38
发布于 2022-09-27 00:36:38
3.8K00
代码可运行
举报
文章被收录于专栏:windliang的博客windliang的博客
运行总次数:0
代码可运行

写过几个油猴脚本,经常对页面请求返回的数据进行拦截或者覆盖,这篇文章就做个总结,涉及到 fetchxhr 两种类型的请求。

环境搭建

先简单写个 html 页面,搭一个 koa 服务进行测试。

html 页面提供一个 id=jsondom 用来加数据,后边我们补充 test.js 文件来请求接口。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        我运行了
        <div id="json"></div>
    </body>
    <script src="test.js"></script>
</html>

html 通过 VSCodelive-server 插件运行在 http://127.0.0.1:5500/ 上。

image-20220823080047006

安装 koakoa-routenode 包,提供一个接口。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const koa = require("koa");
const app = new koa();
const router = require("koa-router")();
router.get("/api/query", async (ctx, next) => {
    ctx.body = {
        data: [1,2,3],
        code: 0,
        msg: "成功",
    };
});
// 跨域
app.use(async (ctx, next) => {
    ctx.set("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
    ctx.set(
        "Access-Control-Allow-Headers",
        "Content-Type, Content-Length, Authorization, Accept, X-Requested-With"
    );
    ctx.set("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS");
    if (ctx.method === "OPTIONS") {
        ctx.body = 200;
    } else {
        await next();
    }
});
// 启动路由
app.use(router.routes());
// 设置响应头
app.use(router.allowedMethods());

// 监听端口
app.listen(3002);

提供了 /api/query 接口,返回 data: [1,2,3], 。运行在本地的 3002 端口上,并且设置跨域,允许从 http://127.0.0.1:5500 访问。

油猴脚本

先简单写一个插入 我是油猴脚本的文本 的脚本,后边再进行修改。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ==UserScript==
// @name         网络拦截测试
// @namespace    https://windliang.wang/
// @version      0.1
// @description  测试
// @author       windliang
// @match        http://127.0.0.1:5500/index.html
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(function () {
    console.log(window.unsafeWindow)
    const dom = document.createElement("div");
    dom.innerText = '我是油猴脚本的文本'
    document.getElementsByTagName("body")[0].append(dom);
})();

此时页面已经被成功拦截:

image-20220823101447976

这里提一句,油猴脚本如果使用 @grant 申请了权限,此时脚本会运行在一个沙箱环境中,如果想访问原始的 window 对象,可以通过 window.unsafeWindow

并且我们加了 @run-at ,让脚本尽快执行。

fetch 请求

html 请求的 test.js 中添加 fetch 的代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fetch("http://localhost:3002/api/query")
  .then((response) => response.json())
  .then((res) => {
  const dom = document.getElementById("json"); 
  dom.innerText = res.data;
});

看下页面,此时就会把 data 显示出来。

image-20220823102924464

如果想更改返回的数据,我们只需要在油猴脚本中重写 fetch 方法,将原数据拿到以后再返回即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ==UserScript==
// @name         网络拦截测试
// @namespace    https://windliang.wang/
// @version      0.1
// @description  测试
// @author       windliang
// @match        http://127.0.0.1:5500/index.html
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(function () {
    console.log(window.unsafeWindow)
    const dom = document.createElement("div");
    dom.innerText = '我是油猴脚本的文本'
    document.getElementsByTagName("body")[0].append(dom);
    const originFetch = fetch;
    console.log(originFetch)
    window.unsafeWindow.fetch = (url, options) => {
        return originFetch(url, options).then(async (response) => {
            console.log(url)
            if(url === 'http://localhost:3002/api/query'){
                const responseClone = response.clone();
                let res = await responseClone.json();
                res.data.push('油猴脚本修改数据')
                const responseNew = new Response(JSON.stringify(res), response);
                return responseNew;
            }else{
                return response;

            }
        });
    };
})();

response 的处理有点绕,当时也是试了好多次才试出了这种方案。

做的事情就是把原来返回的 respones 复制,通过 json 方法拿到数据,进行修改数据,最后新生成一个 Response 进行返回。

看下效果:

image-20220823173813341

成功修改了返回的数据。

xhr

我们将 fetch 改为用 xhr 发送请求,因为页面简单所以请求可能在油猴脚本重写之前就发送了,正常网站不会这么快,所以这里加一个 setTimeout 进行延时。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setTimeout(() => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://localhost:3002/api/query');
    xhr.send();
    xhr.onload = function() {
        const res = JSON.parse(this.responseText);
        const dom = document.getElementById("json");
        dom.innerText = res.data;
    };
}, 0)

fetch 的思路一样,我们可以在返回前更改 responseText

重写 XMLHttpRequest 原型对象的 open 或者 send 方法,在函数内拿到用户当前的 xhr 实例,监听 readystatechange 事件,然后重写 responseText

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const originOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (_, url) {
  if (url === "http://localhost:3002/api/query") {
    this.addEventListener("readystatechange", function () {
      if (this.readyState === 4) {
        const res = JSON.parse(this.responseText);
        res.data.push("油猴脚本修改数据");
        this.responseText = JSON.stringify(res);
      }
    });
  }
  originOpen.apply(this, arguments);
};

运行一下:

image-20220824084014585

拦截失败了,网上搜寻下答案,原因是 responseText 不是可写的,我们将原型对象上的 responseText 属性描述符打印一下。

image-20220824084726967

可以看到 set 属性是 undefined ,因此我们重写 responseText 失败了。

我们无法修改原型对象上的 responseText ,我们可以在当前 xhr 对象,也就是 this 上边定义一个同名的 responseText 属性,赋值的话有两种思路。

1、直接赋值

我们定义一个 writable: true, 的属性,然后直接赋值为我们修改后的数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const originOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (_, url) {
  if (url === "http://localhost:3002/api/query") {
    this.addEventListener("readystatechange", function () {
      if (this.readyState === 4) {
        const res = JSON.parse(this.responseText);
        // 当前 xhr 对象上定义 responseText
        Object.defineProperty(this, "responseText", { 
          writable: true,
        });
        res.data.push("油猴脚本修改数据");
        this.responseText = JSON.stringify(res);
      }
    });
  }

  originOpen.apply(this, arguments);
};

看下页面会发现成功拦截了:

image-20220824085203088

2、重写 get

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const originOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (_, url) {
  if (url === "http://localhost:3002/api/query") {
    const xhr = this;
    const getter = Object.getOwnPropertyDescriptor(
      XMLHttpRequest.prototype,
      "response"
    ).get;
    Object.defineProperty(xhr, "responseText", {
      get: () => {
        let result = getter.call(xhr);
        try {
          const res = JSON.parse(result);
          res.data.push('油猴脚本修改数据')
          return JSON.stringify(res);
        } catch (e) {
          return result;
        }
      },
    });
  }
  originOpen.apply(this, arguments);
};

我们拿到原型对象的 get ,然后在当前对象上定义 responseTextget 属性,修改数据后返回即可。

相比于第一种方案,这种方案无需等待 readystatechange ,在开始的时候重写即可。

需要注意的是,上边方案都只是重写了 responseText 字段,不排除有的网站读取的是 response 字段,但修改的话和上边是一样的,这里就不写了。

通过对 fetchxhr 的重写,我们基本上可以对网页「为所欲为」了,发挥想象力通过油猴脚本应该可以做很多有意思的事情。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 windliang 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
某网络学院学习的”偷懒“思路分析
前几日接到求助,帮朋友完成20课时的网络学习。 我想都没想就接下了,寻思找个接口直接把学习时间提交上去,易如反掌。 最不济最不济,咱还能16x播放,也简单的很。
纯情
2023/04/27
5320
某网络学院学习的”偷懒“思路分析
5 分钟,教你从零快速编写一个油猴脚本!
Tampermonkey,又称 Greasemonkey 油猴脚本,是一款免费的浏览器扩展,可用于管理用户脚本,它本质上是对浏览器接口的二次封装
AirPython
2022/05/22
3.2K0
5 分钟,教你从零快速编写一个油猴脚本!
使用 TamperMonkey 增强生产力
技术人员的日常积累其中的一部分就是总结不同的使用工具。现在各种软件都提供网站形式,在网站场景里,Javascript 是统治语言。TamperMonkey 提供了一种在网站上运行自己脚本的一种方式,应该成为我们工具箱里的一种常用工具。
Dylan Liu
2022/09/07
1K0
油猴脚本编写教程
油猴脚本(Tampermonkey)是一个非常流行的浏览器扩展,它可以运行由广大社区编写的扩展脚本,来实现各式各样的功能,常见的去广告、修改样式文件、甚至是下载视频。今天我们就来看看如何编写自己的油猴脚本。当然为了运行油猴脚本,你应该在浏览器中安装油猴插件。
乐百川
2020/02/18
7.3K0
油猴脚本编写教程
国培计划油猴脚本
国培计划油猴脚本 功能 跳过确认按钮,一直学呀学 视频0.1倍速 老师培训网址http://study.teacheredu.cn // ==UserScript== // @name 2021教师能力提升 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 跳过确认按钮,一直学呀学 // @AuThor bhl // @match http://study.
纯情
2023/04/26
6570
油猴脚本:markdown生成带网页标题的链接
你好,我是喵喵侠。在日常浏览网页和编写Markdown文档时,我们常常需要将网页链接插入到Markdown文档中,并附上网页的标题。然而,手动复制链接和标题不仅耗时,而且容易出错。为了解决这个问题,我们可以编写一个油猴脚本(Tampermonkey Script),自动生成带网页标题的Markdown格式链接。本文将详细介绍如何实现这一功能。
喵喵侠
2024/08/03
2720
油猴脚本:markdown生成带网页标题的链接
从油猴脚本管理器的角度审视Chrome扩展
在之前一段时间,我需要借助Chrome扩展来完成一个需求,当时还在使用油猴脚本与浏览器扩展之间调研了一波,而此时恰好我又有一些做的还可以的油猴脚本 TKScript (点个star吧 😁),相对会比较熟悉脚本管理器的能力,预估是不太能完成需求的,所以趁着这个机会,我又学习了一波浏览器扩展的能力。那么在后来需求的开发过程中,因为有些能力是类似于脚本管理器提供的基础环境,致使我越来越好奇脚本管理器是怎么实现的,而实际上脚本管理器实际上还是一个浏览器扩展,浏览器也并没有给脚本管理器开后门来实现相关能力,而让我疑惑的三个问题是:
WindRunnerMax
2024/05/14
3210
油猴脚本入坑指南
即每个油猴脚本都有的,脚本开头很多行注释的内容,这是油猴脚本关键的基础部分,刚开始接触可能会一头雾水,但你绝不能忽视这部分内容
子润先生
2021/06/09
4.4K0
【前端监控】自动抓取接口请求数据
今天要写的是前端监控SDK的自动抓取接口请求数据。内容不复杂,但是其中会涉及很多细节,不然会踩坑。废话不多说
神仙朱
2021/08/13
2.7K0
【前端监控】自动抓取接口请求数据
从零实现的浏览器Web脚本
在之前我们介绍了从零实现Chrome扩展,而实际上浏览器级别的扩展整体架构非常复杂,尽管当前有统一规范但不同浏览器的具体实现不尽相同,并且成为开发者并上架Chrome应用商店需要支付5$的注册费,如果我们只是希望在Web页面中进行一些轻量级的脚本编写,使用浏览器扩展级别的能力会显得成本略高,所以在本文我们主要探讨浏览器Web级别的轻量级脚本实现。
WindRunnerMax
2023/11/04
8570
油猴脚本去水印实现原理
上周微信公众号推出了一种新的形式,类似小红书这样子,群里有小伙伴调侃,是否是小红书的产品经理跳槽到微信了,那作为一个公众号运营者,我也想利用这一点。那么如何快速设计出好看的小红书封面,便成了我的目标。我在 google 上搜索到了一篇文章,可以通过一个好用的网站,比格设计,设计出好看的封面。它跟稿定设计一样,也是一个在线制图的网站,当然如果你没开会员的话,下载下来也是有水印的。那么我在想,是否也可以写一个油猴脚本来实现白嫖呢?
狂奔滴小马
2023/03/18
1.5K0
油猴脚本去水印实现原理
JS逆向快速定位关键点之9大通用hook脚本
大部分网站都会对关键参数进行加密,JS 逆向时,我们首要任务是定位参数具体的加密逻辑。
Python兴趣圈
2023/11/10
3.5K0
JS逆向快速定位关键点之9大通用hook脚本
常用的js hook 脚本
Cookie Hook 用于定位 Cookie 中关键参数生成位置,以下代码演示了当 Cookie 中匹配到了 v 关键字, 则插入断点
bosh123
2023/12/18
1K0
前端文件下载汇总「案例讲解」
超链接的文件下载考虑到超链接是同源或是跨域情况,读者可通过文章 【案例】同源策略 - CORS 处理熟悉同源策略。
Jimmy_is_jimmy
2024/01/30
3320
前端文件下载汇总「案例讲解」
最新Tampermonkey 中文文档解析(附基础案例和高级案例)
@homepage, @homepageURL, @website and @source
拿我格子衫来
2022/01/24
5.6K0
浏览器的常见考点
这个过程要注意标签位置,以及script标签位置和HTML提供的async defer属性
心谭博客
2020/04/21
1K0
XHR,ajax,axios,fetch傻傻分不清?
AJAX 和 Fetch都可以访问和操纵 HTTP 管道(发出HTTP请求与接收HTTP响应),是解决动态网页的技术方案。
娜姐
2021/11/24
1.9K0
【JS】1942- 你知道 XHR 和 Fetch 的区别吗?
现如今,网站开发普遍采用前后端分离的模式,数据交互成为了不可或缺的关键环节。在这个过程中,XHR 和 Fetch API 是两种最常见的方法,用于从 Web 服务器获取数据。XHR 是一种传统的数据请求方式,而 Fetch API 则代表了现代 Web 开发的新兴标准。接下来,我们将一同深入学习它们的使用方法和适用场景。
pingan8787
2024/02/06
5860
【JS】1942- 你知道 XHR 和 Fetch 的区别吗?
使用 Tampermonkey 编写高级跨网站自动化任务脚本
为了照顾读者中一部分对 Tampermonkey(国内成油猴,以下都简称 TM)不熟悉的读者,这里我借助官方对 TM 的介绍和教程帮助入门用户做以下介绍。
拿我格子衫来
2022/01/24
5.3K0
使用 Tampermonkey 编写高级跨网站自动化任务脚本
Web前端-Ajax基础技术(下)
ajax,web程序是将信息放入公共的服务器,让所有网络用户可以通过浏览器进行访问。
达达前端
2019/07/03
2.6K0
Web前端-Ajax基础技术(下)
相关推荐
某网络学院学习的”偷懒“思路分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验