ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

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

获取页面所有请求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; }
返回列表