当前位置: 首页 > news >正文

获取页面所有请求url和参数插件

测试的时候 , 想获取页面所有的请求, 虽然在network中可以获取到, 但是感觉还是不方便,

所以写了一个插件, 安装之后, 就可以获取到所有的url, 并且可以导出到csv文件中, 可以方便做后需接口测试

目录结构

my-devtools-plugin/
├── manifest.json # 插件配置文件
├── devtools.html # F12 挂载入口网页
├── devtools.js # F12 挂载入口逻辑
├── panel.html # 自定义面板 UI 界面
└── panel.js # 自定义面板核心功能逻辑(录制、去重、CSV导出)

devtolls.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script src="devtools.js"></script> </body> </html>

devtolls.js

// 在 F12 的 Tab 栏最后创建一个名为 "HTTP Logger" 的自定义面板 chrome.devtools.panels.create( "Tay抓取请求", // Tab 上显示的文本 null, // 图标路径(不需要可以传 null) "panel.html", // 面板真正的内容页面 function(panel) { console.log("高级日志面板创建成功!"); } );

manifest.json

{ "manifest_version": 3, "name": "Tay抓取请求", "version": "1.0", "description": "在DevTools中记录并去重页面点击时的HTTP请求,支持CSV导出", "permissions": [ "activeTab", "tabs" ], "devtools_page": "devtools.html" }

panel.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 12px; margin: 0; background-color: #f5f5f5; color: #333; } .controls-container { background: #fff; padding: 12px; border-radius: 6px; border: 1px solid #e0e0e0; margin-bottom: 12px; } .main-controls { display: flex; align-items: center; gap: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; } .filter-controls { display: flex; align-items: center; gap: 15px; padding-top: 10px; font-size: 12px; color: #555; } .switch-container, .filter-item { display: flex; align-items: center; gap: 5px; font-weight: bold; cursor: pointer; } .filter-item { font-weight: normal; } button { padding: 6px 12px; border: 1px solid #ccc; background: #fff; border-radius: 4px; cursor: pointer; font-size: 12px; } button:hover { background: #f0f0f0; border-color: #999; } #delete-btn { background-color: #ff3b30; color: #fff; border: none; } #delete-btn:hover { background-color: #e0241b; } #export-btn { background-color: #0076ff; color: #fff; border: none; font-weight: bold; } #export-btn:hover { background-color: #0062d6; } .table-container { border: 1px solid #e0e0e0; background: #fff; border-radius: 6px; overflow: hidden; } table { width: 100%; border-collapse: collapse; font-size: 12px; text-align: left; } th, td { padding: 8px 10px; border-bottom: 1px solid #eee; vertical-align: top; } th { background: #fafafa; border-bottom: 2px solid #e0e0e0; color: #666; font-weight: 600; } td { word-break: break-all; max-width: 300px; } tr:hover { background-color: #f9f9f9; } .col-select { width: 30px; text-align: center; } .col-method { width: 80px; } .method-badge { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: bold; color: #fff; background: #999; } .method-GET { background-color: #0cbb52; } .method-POST { background-color: #eeaa00; } </style> </head> <body> <div class="controls-container"> <div class="main-controls"> <label class="switch-container"> <input type="checkbox" id="recording-toggle" checked> <span>启用录制</span> </label> <button id="clear-btn">清空数据</button> <button id="delete-btn">删除选中</button> <button id="export-btn">导出为 CSV</button> <span style="font-size: 12px; color: #888;" id="stats">已记录: 0 条</span> </div> <div class="filter-controls"> <strong>过滤器 (勾选表示排除):</strong> <label class="filter-item"> <input type="checkbox" class="filter-checkbox">// ==================== 全局状态与数据结构 ==================== let isRecording = true; const requestMap = new Map(); // 过滤器映射正则 const filterRules = { js: /\.js(\?.*)?$/i, css: /\.css(\?.*)?$/i, images: /\.(png|jpg|jpeg|gif|svg|ico|webp)(\?.*)?$/i, fonts: /\.(woff|woff2|ttf|eot)(\?.*)?$/i }; // DOM 元素引用 const recordingToggle = document.getElementById('recording-toggle'); const clearBtn = document.getElementById('clear-btn'); const deleteBtn = document.getElementById('delete-btn'); const exportBtn = document.getElementById('export-btn'); const selectAllCheckbox = document.getElementById('select-all'); const logTbody = document.getElementById('log-tbody'); const statsSpan = document.getElementById('stats'); // ==================== 事件监听 ==================== // 1. 开关切换 recordingToggle.addEventListener('change', (e) => { isRecording = e.target.checked; }); // 2. 清空全部数据 clearBtn.addEventListener('click', () => { requestMap.clear(); selectAllCheckbox.checked = false; // 重置全选框 renderTable(); }); // 3. 删除选中数据 deleteBtn.addEventListener('click', () => { const checkedBoxes = document.querySelectorAll('.row-checkbox:checked'); if (checkedBoxes.length === 0) { alert('请先勾选需要删除的请求!'); return; } // 遍历所有被勾选的行,从 Map 中物理删除对应的键值对 checkedBoxes.forEach(box => { const urlToDelete = box.getAttribute('data-url'); requestMap.delete(urlToDelete); }); // 重置全选框状态 selectAllCheckbox.checked = false; // 重新渲染表格,UI、数量统计和后续的导出都会自动同步 renderTable(); }); // 4. 表头全选/反选联动 selectAllCheckbox.addEventListener('change', (e) => { const isChecked = e.target.checked; const rowCheckboxes = document.querySelectorAll('.row-checkbox'); rowCheckboxes.forEach(box => { box.checked = isChecked; }); }); // 5. 导出 CSV exportBtn.addEventListener('click', exportToCSV); // 6. 监听网络请求 chrome.devtools.network.onRequestFinished.addListener(function(request) { if (!isRecording) return; const url = request.request.url; const method = request.request.method; if (!url.startsWith('http')) return; // 动态过滤器过滤 if (shouldFilter(request, url)) return; // 解析参数 let paramsText = ''; if (request.request.queryString && request.request.queryString.length > 0) { const queryObj = {}; request.request.queryString.forEach(item => { queryObj[item.name] = item.value; }); paramsText += `[Query]: ${JSON.stringify(queryObj)}\n`; } if (request.request.postData && request.request.postData.text) { paramsText += `[Body]: ${request.request.postData.text}`; } if (!paramsText) { paramsText = '无参数'; } // 去重存储 requestMap.set(url, { method: method, url: url, params: paramsText }); renderTable(); }); // ==================== 核心过滤算法 ==================== function shouldFilter(request, url) { const activeFilters = []; document.querySelectorAll('.filter-checkbox:checked').forEach(checkbox => { activeFilters.push(checkbox.getAttribute('data-type')); }); for (const type of activeFilters) { if (filterRules[type] && filterRules[type].test(url)) return true; const resType = request._resourceType; if (type === 'js' && resType === 'script') return true; if (type === 'css' && resType === 'stylesheet') return true; if (type === 'images' && resType === 'image') return true; if (type === 'fonts' && resType === 'font') return true; } return false; } // ==================== UI 渲染与导出工具函数 ==================== function renderTable() { logTbody.innerHTML = ''; requestMap.forEach((value) => { const tr = document.createElement('tr'); // 1. 选择框列:通过 data-url 属性将多选框绑定到具体的 URL 数据健上 const tdSelect = document.createElement('td'); tdSelect.className = 'col-select'; tdSelect.innerHTML = `<input type="checkbox" class="row-checkbox" data-url="${escapeHTML(value.url)}">`; // 2. 方法列 const tdMethod = document.createElement('td'); tdMethod.className = 'col-method'; tdMethod.innerHTML = `<span class="method-badge method-${value.method}">${value.method}</span>`; // 3. URL 列 const tdUrl = document.createElement('td'); tdUrl.textContent = value.url; // 4. 参数列 const tdParams = document.createElement('td'); tdParams.style.whiteSpace = 'pre-wrap'; tdParams.textContent = value.params; tr.appendChild(tdSelect); tr.appendChild(tdMethod); tr.appendChild(tdUrl); tr.appendChild(tdParams); logTbody.appendChild(tr); }); statsSpan.textContent = `已记录: ${requestMap.size} 条`; } function exportToCSV() { if (requestMap.size === 0) { alert('当前没有可导出的数据!'); return; } const headers = ['Method', 'URL', 'Parameters']; const rows = [headers]; // 因为删除功能已经直接从 requestMap 移除数据了,所以这里导出的数据绝对干净 requestMap.forEach((value) => { rows.push([ escapeCSV(value.method), escapeCSV(value.url), escapeCSV(value.params) ]); }); const csvContent = rows.map(e => e.join(",")).join("\n"); const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", url); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); link.setAttribute("download", `HTTP_Logs_${timestamp}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); } // 安全过滤防止 HTML 属性注入 function escapeHTML(str) { return str.replace(/"/g, '&quot;').replace(/'/g, '&#39;'); } function escapeCSV(val) { if (val === undefined || val === null) return ''; let str = String(val); if (str.includes('"') || str.includes(',') || str.includes('\n') || str.includes('\r')) { str = str.replace(/"/g, '""'); return `"${str}"`; } return str; }
http://www.jsqmd.com/news/1346246/

相关文章:

  • 工程测量自动化:附合导线与四等水准数据处理技术
  • OpenClaw AI智能体框架:从核心原理到实战部署的完整指南
  • 2026年东莞塘厦氧气 氮气 氩气配送选益升气体靠谱 - 城刊速递
  • 小程序扫码解析网页:Jsoup服务端架构与HTML内容提取实践
  • 从信息架构到用户心理:如何写出真正有效的简介
  • 关于从Copilot到Agent——开发工作流正在被颠覆
  • 代理技能扩展_self-improving-agent-skill
  • 聚氨酯封边岩棉夹芯板:严寒地区新选择 - 城刊速递
  • 2026连云港散称干果炒货批发商家测评,避坑指南优选靠谱供货商 - mypinpai
  • 终极Office激活指南:免费解锁Microsoft 365完整功能的3步教程
  • Vue 3实战:构建电影播放详情页与Video.js播放器集成
  • 用普通PC体验macOS:国光黑苹果教程的5大核心价值
  • 医用护具旋钮扣:精细调节与安全固定的技术拆解 - 城刊速递
  • 抖音下载神器:3分钟学会无水印批量下载高清视频
  • 赛博朋克2077存档编辑器终极指南:完全掌控夜之城的免费工具
  • 2026年人员定位系统采购避坑指南:五大核心维度甄选靠谱服务商
  • 怎样轻松掌控窗口尺寸:5个WindowResizer实用技巧指南
  • CAD与网页编辑器数据互通技术方案解析
  • ubuntu vi/vim配置
  • 2026年上海工业垃圾清运与废旧金属回收服务怎么选?专业机构能力对比与选择指南 - 优质品牌商家
  • C/C++函数指针全解析:从回调机制到设计模式底层实现
  • 网卡驱动RTL8821CU移植
  • Java多线程实战:从锁机制到JUC并发工具与线程池调优
  • UE4级联阴影(CSM)原理与优化:解决大场景阴影性能与质量难题
  • 思源宋体TTF:免费专业中文字体的正确打开方式
  • 资料分析核心概念与速算技巧:从基期现期到增长率实战应用
  • android开发 在所有activity启动时主动隐藏导航栏,上划时能唤出导航栏功能
  • 2026年度吸能蜂窝批发厂家信赖品牌**单 - 城刊速递
  • 2026晾衣架安装企业十大热门工作室真实横评,价格透明不交智商税 - mypinpai
  • 数控电源-恒压/恒流,STC32G-HSPWM做BUCK降压式开关电源-PID控制