FPF   付鹏篚的笔记
  • 主页
  • 归档
  • 分类
  • 标签
  • 关于

付鹏篚

  • 主页
  • 归档
  • 分类
  • 标签
  • 关于
Quiet主题
  • JavaScript
  • tampermonkey

tampermonkey油猴

付鹏篚
Tool

2023-06-18 20:00:00

文章目录
  1. 1. 脚本元信息(Metadata)
  2. 2. 初始化变量
  3. 3. startup 函数
    1. 3.1 exportFile 函数
    2. 3.2 addButton 函数
    3. 3.3 updateDownloaderDiv 函数
    4. 3.4 removeHtmlFromJson 函数
    5. 3.5 重写 XMLHttpRequest.prototype.open
  4. 4. 其他功能
    1. 4.1 checkRequestStatus 函数
    2. 4.2 sendRequest 函数
    3. 4.3 定时器和初始化
  5. 总结

1. 脚本元信息(Metadata)

// ==UserScript==
// @name       租租车
// @namespace  tacatman@gmail.com/scripts
// @version    1.0
// @description 监听所有 Ajax 请求和响应,并在屏幕上渲染一个按钮,允许下载(按请求分组)。以 Mockserver 格式导出。
// @author     newbieFPF
// @license    GPL
// @match      *://w.zuzuche.com/*
// @grant      none
// @run-at     document-start
// @icon       https://global.zuzuche.com/assets/images/common/zzc-logo-0119.png?1677569026
// @require    https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js
// ==/UserScript==
  • @name: 脚本的名称,这里是“租租车”。
  • @namespace: 脚本的命名空间,用于唯一标识脚本。
  • @version: 脚本的版本号。
  • @description: 脚本的功能描述。该脚本监听所有 Ajax 请求和响应,并在页面上渲染一个按钮,允许用户下载请求数据(按请求分组),并以 Mockserver 格式导出。
  • @author: 脚本的作者。
  • @license: 脚本的许可证,这里是 GPL。
  • @match: 脚本生效的 URL 模式。这里匹配 w.zuzuche.com 域名下的所有页面。
  • @grant: 声明脚本需要的特殊权限。这里没有特殊权限需求,因此为 none。
  • @run-at: 脚本的运行时机。document-start 表示在文档加载的早期阶段运行。
  • @icon: 脚本的图标 URL。
  • @require: 脚本依赖的外部库。这里引入了 FileSaver.js,用于实现文件下载功能。

2. 初始化变量

window.requestSnifferForMock = {};
window.requestSnifferForMockSaveAs = saveAs;
var isRequestInProgress = false;
var anotherVariable = [];
var intervalRequest = null;
  • window.requestSnifferForMock: 用于存储所有捕获的请求和响应数据。
  • window.requestSnifferForMockSaveAs: 将 FileSaver.js 的 saveAs 方法挂载到全局对象,方便后续调用。
  • isRequestInProgress: 标志位,用于判断当前是否有请求正在进行。
  • anotherVariable: 用于存储额外的请求数据。
  • intervalRequest: 用于存储定时器的 ID,方便后续清除。

3. startup 函数

startup 是脚本的核心逻辑,负责监听 Ajax 请求并处理响应。

3.1 exportFile 函数

var exportFile = function (key) {
    var blob = new Blob([JSON.stringify(window.requestSnifferForMock[key], null, 2)], {type : 'application/json'});
    requestSnifferForMockSaveAs(blob, key + ".json");
};
  • 功能:将捕获的请求数据导出为 JSON 文件。
  • 参数:key 是请求的路径(如 /list_new_json_5.php)。
  • 实现:
    • 使用 JSON.stringify 将数据转换为 JSON 字符串。
    • 使用 Blob 创建一个文件对象。
    • 调用 FileSaver.js 的 saveAs 方法将文件保存到本地。

3.2 addButton 函数

var addButton = function(text, func) {
    var div = document.createElement('div');
    div.setAttribute('style', 'padding: 10px; display: flex;');
    var button = document.createElement('button');
    button.setAttribute('style', 'flex: 1 0 auto;width: 90px;height: 30px;line-height: 30px;border: none;color: #fff;background-color: #4e7cdd;border-radius: 3px;text-align: center;cursor: pointer;' );
    button.addEventListener('click', func, false);
    button.innerText = text;
    div.appendChild(button);
    return div;
};
  • 功能:创建一个按钮,并绑定点击事件。
  • 参数:
    • text: 按钮显示的文本。
    • func: 按钮点击时触发的函数。
  • 实现:
    • 创建一个 div 容器和一个 button 元素。
    • 设置按钮的样式和点击事件。
    • 将按钮添加到 div 中并返回。

3.3 updateDownloaderDiv 函数

var updateDownloaderDiv = function() {
    const element = document.getElementById('requestSnifferForMockDiv');
    if (Object.keys(window.requestSnifferForMock).length > 0) {
        element.innerHTML = "";
        Object.keys(window.requestSnifferForMock).forEach((t) => {
            element.appendChild(addButton('  发送请求('+ window.requestSnifferForMock[t].length +')  ', exportFile.bind(undefined, t)));
        });
    } else {
        element.innerHTML = "loading...";
    }
};
  • 功能:更新页面上的按钮容器,显示捕获的请求数据。
  • 实现:
    • 获取按钮容器的 DOM 元素。
    • 如果捕获到请求数据,为每个请求路径创建一个按钮,并绑定导出功能。
    • 如果没有数据,显示“loading…”。

3.4 removeHtmlFromJson 函数

var removeHtmlFromJson = function (jsonData) {
    function removeHtml(text) {
        var doc = new DOMParser().parseFromString(text, 'text/html');
        return doc.body.textContent || "";
    }

    function removeHtmlRecursive(obj) {
        if (typeof obj === 'string') {
            return removeHtml(obj);
        } else if (Array.isArray(obj)) {
            return obj.map(item => removeHtmlRecursive(item));
        } else if (typeof obj === 'object' && obj !== null) {
            var newObj = {};
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    newObj[key] = removeHtmlRecursive(obj[key]);
                }
            }
            return newObj;
        } else {
            return obj;
        }
    }

    return removeHtmlRecursive(jsonData);
}
  • 功能:递归地移除 JSON 数据中的 HTML 标签。
  • 实现:
    • 使用 DOMParser 解析字符串并提取纯文本。
    • 递归处理数组和对象,确保所有字符串都被清理。

3.5 重写 XMLHttpRequest.prototype.open

XMLHttpRequest.prototype.open = function() {
    const request = {
        method: arguments['0']
    };
    const url = arguments['1'];
    if (url.includes('/list_new_json_5.php')) {
        isRequestInProgress = true;
        const index = url.indexOf('?');
        if (index >= 0) {
            request.path = url.substring(0, index);
            request.queryStringParameters = url.substring(url.indexOf('?') + 1).split('&').map((t) => t.split('=')).reduce((agg, t) => ({ ...agg, [t[0]] : [t[1]]}) , {});
        } else {
            request.path = url;
        }
        this.addEventListener("load", () => {
            if (window.requestSnifferForMock[request.path] == null) {
                window.requestSnifferForMock[request.path] = [];
            }
            window.requestSnifferForMock[request.path].push({
                httpRequest: request,
                httpResponse: {
                    statusCode: this.status,
                    body: this.responseType === 'json' ? JSON.stringify(this.response) : this.responseText,
                },
            });
            fetch('https://w.zuzuche.com'+ url, {
                method: 'get',
            })
                .then(response => response.json())
                .then(res => {
                    anotherVariable.push(res);
                    isRequestInProgress = false;
                    resetInterval();
                })
                .catch(error => {
                    clearInterval(intervalRequest);
                    console.error(error);
                });

            updateDownloaderDiv();
        });
    }

    XMLHttpRequestOpen.apply(this, arguments);
};
  • 功能:重写 XMLHttpRequest 的 open 方法,监听特定请求并捕获响应数据。
  • 实现:
    • 解析请求的 URL 和方法。
    • 如果 URL 包含 /list_new_json_5.php,则监听请求的 load 事件。
    • 将请求和响应数据存储到 window.requestSnifferForMock 中。
    • 发送一个额外的请求以获取更多数据,并更新按钮容器。

4. 其他功能

4.1 checkRequestStatus 函数

var checkRequestStatus = function() {
    if (isRequestInProgress) {
        console.log('请求仍在进行中');
    } else {
        console.log('请求已完成');
        if(window.requestSnifferForMock['/list_new_json_5.php'].length > 1){
            sendRequest();
        }else{
            console.error("请求错误");
        }
        clearInterval(intervalRequest);
    }
}
  • 功能:检查请求状态,如果请求完成则调用 sendRequest。

4.2 sendRequest 函数

var sendRequest = function() {
    const requestData = anotherVariable[anotherVariable.length-1];
    fetch('https://avisapi.56zhiyun.com/export', {
        method: 'post',
        headers: {
            'Content-Type': 'text/plain',
        },
        body: JSON.stringify(requestData),
    })
        .then(response => {
            console.log('接收响应数据:', response);
            if (!response.ok) {
                throw new Error('请求出错');
            }
            return response.text();
        })
        .then(data => {
            const url = data;
            if (url) {
                window.location.href = url;
            } else {
                console.error('未找到有效的数据URL');
            }
        })
        .catch(error => {
            console.error('接收的数据请求出错:', error);
        });
};
  • 功能:将捕获的数据发送到指定 API,并处理响应。

4.3 定时器和初始化

var intervalId = setInterval(function () {
    if (document.body) {
        clearInterval(intervalId);
        var div = document.createElement('div');
        div.setAttribute('id', 'requestSnifferForMockDiv');
        div.setAttribute('style', 'text-align:center;color:#4e7cdd;background-color: yellow;padding:12px 24px;border-radius: 8px;  position: fixed; z-index: 9999; top: 120px;');
        document.body.appendChild(div);
        startup();
    }
}, 100);

function resetInterval() {
    clearInterval(intervalRequest);
    intervalRequest = setInterval(checkRequestStatus, 6000);
}
  • 功能:在页面加载完成后初始化脚本,并设置定时器检查请求状态。

总结

该脚本的核心功能是监听特定 Ajax 请求,捕获请求和响应数据,并将其以 Mockserver 格式导出。通过重写 XMLHttpRequest 的 open 方法,实现了对请求的拦截和处理。同时,脚本在页面上动态创建按钮,方便用户下载数据。

上一篇

OCR(光学字符识别)

下一篇

Vue 动态路由配置与权限控制

©2025 By 付鹏篚. 主题:Quiet
Quiet主题