油猴腳本高級應用:攔截與修改網頁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 );}});});})
})();