油猴脚本高级应用:拦截与修改网页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 callback
XMLHttpRequest.callbacks.push( callback );
} else {
// create a callback queue
XMLHttpRequest.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 though
for( 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 );
}
});
});
})
})();