You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么用户脚本无法拦截YouTube页面的部分请求?

为什么用户脚本无法拦截YouTube页面的部分请求?

提问内容

类似[相关问题],我写了一个用户脚本来拦截fetch和XMLHttpRequest请求并在控制台打印URL,但它没能拦截所有请求——比如以下两个资源的请求就没有出现在日志里:

  • https://www.youtube.com/s/desktop/508deff1/img/logos/favicon.ico
  • https://tpc.googlesyndication.com/sodar/56-y-0RG.js

我的脚本代码如下:

// ==UserScript==
// @name         YouTube Urls
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  URLs on YouTube.
// @author       me
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // intercept fetch requests
    const originalFetch = window.fetch;

    window.fetch = function(url, options) {

        console.log(url)
        return originalFetch(url, options);
    };


    // intercept XMLHttpRequest
    const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, async, user, password) {

        console.log(url)
        return originalXhrOpen.apply(this, arguments);
    };

})();

这两个资源都不是预加载的:
资源加载情况截图

编辑补充:
以下是这几个请求的调用栈信息:
第一个请求的调用栈:

(anonymous)         @desktop_polymer.js:7320
zla                 @base.js:1425
nwa                 @base.js:2869
mya                 @base.js:3155
g.k.xz              @base.js:9573
g.k.startRendering  @base.js:9520
.
.
.
g.k.loadVideoByPlayerVars   @base.js:10439
g.k.saa                     @base.js:10612
UR.a.state.j.<computed>     @base.js:4605
(anonymous)                 @desktop_polymer.js:9647
(anonymous)                 @desktop_polymer.js:18592
acquireApi_                 @desktop_polymer.js:18592

(anonymous)                 @desktop_polymer.js:3853
(anonymous)                 @desktop_polymer.js:11370
Ov.resolveCommand           @desktop_polymer.js:5622
fmb                         @desktop_polymer.js:7712
onEndpointClick_            @desktop_polymer.js:7719
(anonymous)                 @desktop_polymer.js:3853

第二个请求的调用栈截图:
第二个请求调用栈

第三个请求的调用栈:

eval                @ VM118:1
gy                  @ VM110:1
t7                  @ VM114:1
eval                @ 5k7CCto5.html:1
Oa                  @ 5k7CCto5.html:1
M5                  @ 5k7CCto5.html:1
X                   @ 5k7CCto5.html:1
eval                            @ 5k7CCto5.html:1
requestIdleCallback.timeout     @ 5k7CCto5.html:1
requestIdleCallback
R3                     @ 5k7CCto5.html:1
M5                     @ 5k7CCto5.html:1
X                      @ 5k7CCto5.html:1
eval                   @ 5k7CCto5.html:1
requestIdleCallback.timeout     @ 5k7CCto5.html:1
requestIdleCallback
R3                     @ 5k7CCto5.html:1
M5                     @ 5k7CCto5.html:1
X                      @ 5k7CCto5.html:1
eval                   @ 5k7CCto5.html:1
requestIdleCallback.timeout     @ 5k7CCto5.html:1
requestIdleCallback
R3                     @ 5k7CCto5.html:1
M5                     @ 5k7CCto5.html:1
X                      @ 5k7CCto5.html:1
eval                   @ 5k7CCto5.html:1
requestIdleCallback.timeout @ 5k7CCto5.html:1
requestIdleCallback
R3                     @ 5k7CCto5.html:1
M5                     @ 5k7CCto5.html:1
X                      @ 5k7CCto5.html:1
eval                   @ 5k7CCto5.html:1
requestIdleCallback.timeout     @ 5k7CCto5.html:1
requestIdleCallback
R3                     @ 5k7CCto5.html:1
M5                     @ 5k7CCto5.html:1
X                      @ 5k7CCto5.html:1
.
.
.
Promise.then
C               @ 5k7CCto5.html:13
D               @ 5k7CCto5.html:13
db              @ 5k7CCto5.html:19
E               @ 5k7CCto5.html:18
(anonymous)             @ 5k7CCto5.html:13
(anonymous)             @ 5k7CCto5.html:41
(anonymous)             @ 5k7CCto5.html:37

我的回答

咱们一步步拆解这个问题,你的脚本只覆盖了fetch和XMLHttpRequest.open,但有些请求没被捕获,主要是这几个原因:

1. 部分请求不走fetch/XMLHttpRequest通道

  • 浏览器自动发起的请求:比如favicon.ico,这是浏览器为加载网站图标自动发起的请求,完全不经过JS层面的fetch或XHR接口,你的拦截代码根本碰不到它。
  • 第三方上下文的请求:像tpc.googlesyndication.com的广告脚本,大概率是在隔离的iframe或沙箱环境里运行的。你的脚本只匹配了youtube.com/*,没法侵入第三方域名的JS上下文,自然抓不到那边的请求调用。
  • Service Worker接管的请求:如果YouTube用了Service Worker处理资源请求,这些请求会直接走Service Worker的独立上下文,不会经过你在主页面替换的fetch方法。

2. 你的fetch拦截没处理Request对象的情况

你现在的代码直接console.log(url),但如果页面里的代码调用fetch时传的是Request实例(而非字符串URL),控制台只会输出一个Request对象,不是直观的URL字符串,你可能会误以为没拦截到。


优化后的解决方案

针对这些问题,给你两个优化方向:

方案一:完善JS发起请求的拦截逻辑

修改fetch拦截来支持Request对象,同时加上对其他请求API的拦截:

// ==UserScript==
// @name         YouTube Urls
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  URLs on YouTube.
// @author       me
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // 处理fetch请求,兼容Request对象
    const originalFetch = window.fetch;
    window.fetch = function(input, options) {
        // 解析真实URL:如果是Request实例就取url属性,否则直接用字符串
        const requestUrl = typeof input === 'string' ? input : input.url;
        console.log('[Fetch]', requestUrl);
        return originalFetch(input, options);
    };

    // 拦截XMLHttpRequest
    const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
        console.log('[XHR]', url);
        return originalXhrOpen.apply(this, arguments);
    };

    // 拦截navigator.sendBeacon请求
    const originalSendBeacon = navigator.sendBeacon;
    navigator.sendBeacon = function(url, data) {
        console.log('[Beacon]', url);
        return originalSendBeacon.apply(this, arguments);
    };

})();

方案二:用PerformanceObserver捕获所有资源请求

如果想无差别捕获所有资源加载(包括浏览器自动发起、第三方脚本、Service Worker处理的请求),PerformanceObserver是更靠谱的选择——它能监听页面上所有的资源加载事件,不管请求是怎么发起的:

// ==UserScript==
// @name         YouTube Urls
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  URLs on YouTube.
// @author       me
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // 监听所有资源加载事件
    const resourceObserver = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
            if (entry.entryType === 'resource') {
                console.log('[所有资源]', entry.name);
            }
        }
    });
    // 启动观察者
    resourceObserver.observe({ entryTypes: ['resource'] });

    // 保留原有的JS请求拦截,方便查看细节
    const originalFetch = window.fetch;
    window.fetch = function(input, options) {
        const requestUrl = typeof input === 'string' ? input : input.url;
        console.log('[JS Fetch]', requestUrl);
        return originalFetch(input, options);
    };

    const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
        console.log('[JS XHR]', url);
        return originalXhrOpen.apply(this, arguments);
    };

})();

额外说明

  • 对于第三方iframe的请求,除非你给脚本添加对应的@match规则(比如*://tpc.googlesyndication.com/*),否则没法直接拦截它的JS上下文——不过浏览器的跨域安全策略会限制这种操作,所以用PerformanceObserver是更稳妥的方式。
  • 如果你怀疑Service Worker在干扰,可以在Chrome开发者工具的Application面板里临时禁用Service Worker,再测试脚本效果。

备注:内容来源于stack exchange,提问作者user2495207

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:59:38