油猴脚本高级应用:拦截与修改网页Fetch请求实战指南
简介:
本文介绍了几个使用油猴(Tampermonkey)脚本拦截和修改网页 fetch
请求的案例。这些脚本可以在浏览器扩展油猴中运行,用于开发者调试网络请求或自定义页面行为。
核心内容:
- fetch 拦截案例2:通过覆盖原生
fetch
函数,拦截请求并修改响应的json
方法,添加自定义属性hook
。但此方法可能会漏掉一些请求。 - fetch 拦截案例1:同样覆盖原生
fetch
,但通过检查请求的URL,只拦截特定域名(如bilivideo.com
)的请求,其他请求正常放行,避免误拦截。 - 国外大神脚本:一个示例脚本框架,展示了油猴脚本的基本结构和元数据,包括运行时机、脚本名称、描述、作者信息等。此脚本实际功能尚未实现,但提供了一个扩展和自定义的起点。
使用场景:
- 网络调试:开发者可以在开发过程中使用这些脚本来拦截和分析网络请求,方便定位问题。
- 页面自定义:用户可以通过修改响应数据,实现对网页内容的个性化定制。
- 学习参考:油猴脚本的示例为学习如何使用油猴扩展和JavaScript进行浏览器自动化提供了参考。
注意事项:
- 脚本运行时机(
@run-at
)设置为document-start
,确保尽早覆盖原生函数。 - 使用
@grant unsafeWindow
声明,授予脚本访问或修改全局窗口对象的权限。 - 脚本可能需要根据实际网页结构和需求进行调整和完善。
fetch 拦截请求案例2(会漏掉一些请求)
// @run-at document-start
// @grant unsafeWindow
(function () { let oldfetch = fetch;function fuckfetch() {return new Promise((resolve, reject) => {//console.log("arguments",arguments)oldfetch.apply(this, arguments).then(response => {const oldJson = response.json;console.log(response.url)response.json = function() {return new Promise((resolve, reject) => {oldJson.apply(this, arguments).then(result => {result.hook = 'success';resolve(result);});});};resolve(response);});});}unsafeWindow.fetch = fuckfetch;})();
fetch 拦截请求案例1(会漏掉一些请求)
把运行时间设置为document-start,确保能拦截到较早发出的请求。
这里我通过url判断是否为获取b站直播流的请求,如果不是则不拦截,避免误伤。
// @run-at document-start
// @grant unsafeWindow
(function () { const originFetch = fetch;unsafeWindow.fetch = (...arg) => {console.log('fetch arg', ...arg);if (arg[0].indexOf('bilivideo.com') > -1) {//console.log('拦截直播流')return new Promise(() => {throw new Error();});} else {//console.log('通过')return originFetch(...arg);}}
})();
国外大神的脚本(最能拦截所有)
来自国外大神的脚本,直接执行即可
// ==UserScript==
// @run-at document-start
// @name CSDN-创作中心
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author wusp
// @match https://mp.csdn.net/*
// @exclude 这个和 iclude 配合使用,排除匹配到的网址,优先于 include
// @require http://code.jquery.com/jquery-1.11.0.min.js
// @grant unsafeWindow
// ==/UserScript==(function () {'use strict';$(() => { 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内的回调函数// call the native send()oldSend.apply(this, arguments);// 由于我们获取了send函数的引用,并且复写了send函数,这样我们在调用原send的函数的时候,需要对其传入引用,而arguments是传入的参数}}}// e.g.addXMLRequestCallback( function( xhr ) {// 调用劫持函数,填入一个function的回调函数// 回调函数监听了对xhr调用了监听load状态,并且在触发的时候再次调用一个function,进行一些数据的劫持以及修改xhr.addEventListener("load", function(){if ( xhr.readyState == 4 && xhr.status == 200 ) {console.log( xhr.responseURL );}});});})
})();