油猴脚本无法拦截请求

我写了个油猴脚本
已经指定

@run-at       document-start 

但是发现 想通过以下方法拦截请求时,还是一些请求比我的脚本更快。这是什么原因呢?

 const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
     
        return originalXhrOpen.apply(this, [method, url, ...rest]);
    };
3 个赞

油猴肯定后于网站加载,点击事件可以拦截,默认加载的接口一般拦截不掉吧

我想获取淘宝/天猫当前页面的商品信息,虽然淘宝天猫都有/mtop.taobao.pcdetail.data.get/1.0/ 这个请求,但是发现天猫的请求比我脚本更快。目前只能抓到淘宝的,抓不到天猫的。
完整代码如下:

// ==UserScript==
// @name         Taobao 信息获取
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  获取商品信息
// @match        https://item.taobao.com/*
// @match        https://detail.tmall.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    let productPriceInfo = '';  // 用于存储产品价格信息的变量

    // 处理 Taobao 数据的函数
    function processTaobaoData(taobao) {
        try {
            const skuInfo = taobao.data.skuCore.sku2info;
            const skus = taobao.data.skuBase.skus;
            const props = taobao.data.skuBase.props;

            skus.forEach(sku => {
                const skuId = sku.skuId;
                const propPaths = sku.propPath.split(';');

                let productDetails = [];

                // 遍历 propPaths 数组,匹配每一个 propPath
                propPaths.forEach(propPath => {
                    const [pid, vid] = propPath.split(':');
                    const prop = props.find(p => p.pid === pid);

                    if (prop) {
                        const value = prop.values.find(v => v.vid === vid);
                        if (value) {
                            productDetails.push({
                                propName: prop.name,
                                valueName: value.name,
                            });
                        }
                    }
                });

                if (skuInfo[skuId]) {
                    let priceText = skuInfo[skuId].price.priceText
                    const subPrice = skuInfo[skuId].subPrice
                    if (subPrice) {
                        priceText = subPrice.priceText;
                    }
                    let detailText = ""
                    productDetails.forEach(detail => {
                        detailText += `${detail.propName}: ${detail.valueName} \t`
                    });
                    console.log(`SKU ID: ${skuId}, Price: ${priceText}, Details: ${detailText}`);

                    productPriceInfo += `${skuId}\t${priceText}\t${detailText}\n`;
                }
            });

            addCopyButton();  // 解析完成后添加按钮

        } catch (error) {
            console.error('Error processing Taobao data:', error);
        }
    }

    function addCopyButton() {
        // 创建按钮
        const copyButton = document.createElement('button');
        copyButton.innerText = '复制商品价格';
        copyButton.style.position = 'fixed';
        copyButton.style.right = '20px';
        copyButton.style.top = '50%';
        copyButton.style.fontSize = '40px';
        copyButton.style.transform = 'translateY(-50%)';
        copyButton.style.padding = '10px 20px';
        copyButton.style.backgroundColor = '#ff5000';
        copyButton.style.color = '#fff';
        copyButton.style.border = 'none';
        copyButton.style.borderRadius = '5px';
        copyButton.style.cursor = 'pointer';
        copyButton.style.zIndex = '1000';

        // 将按钮添加到页面
        document.body.appendChild(copyButton);

        // 绑定点击事件,复制内容到剪贴板
        copyButton.addEventListener('click', () => {
            if (productPriceInfo) {
                navigator.clipboard.writeText(productPriceInfo).then(() => {
                    // 修改按钮文字提示用户已复制成功
                    const originalText = copyButton.innerText;
                    copyButton.innerText = '复制成功!';

                    // 1 秒后恢复按钮原始文字
                    setTimeout(() => {
                        copyButton.innerText = originalText;
                    }, 1000);
                }, () => {
                    copyButton.innerText = '复制失败';
                    setTimeout(() => {
                        copyButton.innerText = '复制商品价格';
                    }, 1000);
                });
            } else {
                copyButton.innerText = '无可复制内容';
                setTimeout(() => {
                    copyButton.innerText = '复制商品价格';
                }, 1000);
            }
        });
    }

    // 拦截请求并处理数据
    const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
        if (url.includes('/mtop.taobao.pcdetail.data.get/1.0/')) {
            this.addEventListener('load', function () {
                if (this.readyState === 4 && this.status === 200) {
                    try {
                        const response = JSON.parse(this.responseText);
                        processTaobaoData(response);
                    } catch (e) {
                        console.error('Failed to parse Taobao response:', e);
                    }
                }
            });
        }
        return originalXhrOpen.apply(this, [method, url, ...rest]);
    };
})();

不是的,有个指定的方法是 document-start ,我 debug 的调试的时候发现我的脚本加载的时间是比页面靠前的,但是XMLHttpRequest内的拦截却滞后。

已经知道原因了,因为天猫的这个请求使用的在 js 中的(jsoup)。

但是淘宝属于 api 请求(XMLHttpRequest)

目前无法 hook 到 js 文件的加载,所以暂时无法 hook 到天猫的数据。

道高一尺魔高一丈 :tieba_025:

function log(func) {
  return function() {
    console.log(arguments);
    return func.apply(this, arguments);
  };
}

let __mtopjsonp1;
Object.defineProperty(window, 'mtopjsonp1', {
    get: function () {
        return __mtopjsonp1;
    },
    set: function (mtopjsonp1) {
        __mtopjsonp1 = log(mtopjsonp1);
    }
});
1 个赞

好使。给你点个大大的赞!!!!!!!!

image

From 快问快答 to 开发调优