diff --git a/Tampermonkey/PageDebugHelper.js b/Tampermonkey/PageDebugHelper.js new file mode 100644 index 0000000..635b11e --- /dev/null +++ b/Tampermonkey/PageDebugHelper.js @@ -0,0 +1,522 @@ +// ==UserScript== +// @name Universal Page Inspector +// @namespace http://tampermonkey.net/ +// @version 1.3 +// @description 通用页面检查器,获取页面各种信息 +// @author You +// @match *://*/* +// @grant GM_xmlhttpRequest +// @grant GM_getValue +// @grant GM_setValue +// @grant GM_addStyle +// ==/UserScript== + +(function() { + 'use strict'; + + // 创建检查按钮 + const inspectBtn = document.createElement('button'); + inspectBtn.textContent = '🔍 检查页面'; + inspectBtn.id = 'page-inspector-btn'; + + inspectBtn.style.position = 'fixed'; + inspectBtn.style.top = '8%'; + inspectBtn.style.right = '1%'; + inspectBtn.style.padding = '8px 16px'; + inspectBtn.style.backgroundColor = '#007bff'; + inspectBtn.style.color = 'white'; + inspectBtn.style.border = 'none'; + inspectBtn.style.borderRadius = '8px'; + inspectBtn.style.cursor = 'pointer'; + inspectBtn.style.zIndex = '99999'; + inspectBtn.style.fontSize = '12px'; + inspectBtn.style.fontWeight = 'bold'; + + // 创建结果弹窗 + const createInspectPopup = (content) => { + // 移除旧的弹窗 + const oldPopup = document.getElementById('inspector-popup'); + if (oldPopup) oldPopup.remove(); + + // 创建新弹窗 + const popup = document.createElement('div'); + popup.id = 'inspector-popup'; + popup.style.position = 'fixed'; + popup.style.top = '50%'; + popup.style.left = '50%'; + popup.style.transform = 'translate(-50%, -50%)'; + popup.style.width = '85%'; + popup.style.height = '85%'; + popup.style.backgroundColor = '#ffffff'; + popup.style.border = '2px solid #444'; + popup.style.borderRadius = '12px'; + popup.style.boxShadow = '0 0 60px rgba(0,0,0,0.3)'; + popup.style.zIndex = '100000'; + popup.style.overflow = 'hidden'; + popup.style.display = 'flex'; + popup.style.flexDirection = 'column'; + + // 标题栏 + const titleBar = document.createElement('div'); + titleBar.style.padding = '12px 20px'; + titleBar.style.backgroundColor = '#2c3e50'; + titleBar.style.color = 'white'; + titleBar.style.fontWeight = 'bold'; + titleBar.style.display = 'flex'; + titleBar.style.justifyContent = 'space-between'; + titleBar.style.alignItems = 'center'; + + const title = document.createElement('span'); + title.textContent = '页面检查器'; + titleBar.appendChild(title); + + const closeBtn = document.createElement('button'); + closeBtn.textContent = '✕'; + closeBtn.style.background = 'none'; + closeBtn.style.border = 'none'; + closeBtn.style.color = 'white'; + closeBtn.style.cursor = 'pointer'; + closeBtn.style.fontSize = '18px'; + closeBtn.style.padding = '0 8px'; + closeBtn.addEventListener('click', () => popup.remove()); + titleBar.appendChild(closeBtn); + + // 选项卡容器 + const tabContainer = document.createElement('div'); + tabContainer.style.display = 'flex'; + tabContainer.style.backgroundColor = '#34495e'; + tabContainer.style.padding = '0'; + tabContainer.style.gap = '1px'; + tabContainer.style.overflowX = 'auto'; + + // 内容容器 + const contentContainer = document.createElement('div'); + contentContainer.style.flex = '1'; + contentContainer.style.display = 'flex'; + contentContainer.style.flexDirection = 'column'; + contentContainer.style.overflow = 'hidden'; + + // 创建选项卡 + const createTab = (id, label, content) => { + const tab = document.createElement('button'); + tab.textContent = label; + tab.dataset.tab = id; + tab.style.padding = '10px 16px'; + tab.style.backgroundColor = '#34495e'; + tab.style.color = '#ecf0f1'; + tab.style.border = 'none'; + tab.style.cursor = 'pointer'; + tab.style.whiteSpace = 'nowrap'; + tab.style.fontSize = '13px'; + tab.style.fontWeight = 'bold'; + tab.style.transition = 'background-color 0.2s'; + + tab.addEventListener('click', () => { + // 移除所有激活状态 + tabContainer.querySelectorAll('button').forEach(t => { + t.style.backgroundColor = '#34495e'; + }); + // 激活当前选项卡 + tab.style.backgroundColor = '#2c3e50'; + // 显示对应内容 + contentContainer.querySelectorAll('.tab-content').forEach(c => { + c.style.display = 'none'; + }); + contentContainer.querySelector(`#tab-${id}`).style.display = 'block'; + }); + + // 创建内容区域 + const tabContent = document.createElement('div'); + tabContent.id = `tab-${id}`; + tabContent.className = 'tab-content'; + tabContent.style.display = 'none'; + tabContent.style.flex = '1'; + tabContent.style.padding = '20px'; + tabContent.style.overflow = 'auto'; + tabContent.style.fontFamily = 'Consolas, Monaco, monospace'; + tabContent.style.fontSize = '13px'; + tabContent.style.lineHeight = '1.6'; + tabContent.style.whiteSpace = 'pre-wrap'; + tabContent.style.color = '#2c3e50'; + tabContent.textContent = content; + + contentContainer.appendChild(tabContent); + + return tab; + }; + + // 添加选项卡 + const tabs = [ + createTab('overview', '概览', content.overview), + createTab('elements', '元素检查', content.elements), + createTab('images', '图片分析', content.images), + createTab('links', '链接分析', content.links), + createTab('storage', '存储信息', content.storage), + createTab('network', '网络信息', content.network), + createTab('performance', '性能信息', content.performance), + createTab('console', '控制台', content.console) + ]; + + // 设置第一个选项卡为激活状态 + if (tabs[0]) { + tabs[0].style.backgroundColor = '#2c3e50'; + contentContainer.querySelector('#tab-overview').style.display = 'block'; + } + + // 添加选项卡到容器 + tabs.forEach(tab => tabContainer.appendChild(tab)); + + // 复制按钮 + const copyBtn = document.createElement('button'); + copyBtn.textContent = '📋 复制当前'; + copyBtn.style.position = 'absolute'; + copyBtn.style.top = '15px'; + copyBtn.style.right = '60px'; + copyBtn.style.padding = '6px 12px'; + copyBtn.style.backgroundColor = '#27ae60'; + copyBtn.style.color = 'white'; + copyBtn.style.border = 'none'; + copyBtn.style.borderRadius = '5px'; + copyBtn.style.cursor = 'pointer'; + copyBtn.style.fontSize = '12px'; + copyBtn.style.fontWeight = 'bold'; + copyBtn.addEventListener('click', () => { + const activeTab = tabContainer.querySelector('button[style*="background-color: rgb(44, 62, 80)"]'); + const tabId = activeTab ? activeTab.dataset.tab : 'overview'; + const activeContent = content[tabId]; + navigator.clipboard.writeText(activeContent).then(() => { + const originalText = copyBtn.textContent; + copyBtn.textContent = '✅ 已复制!'; + setTimeout(() => { + copyBtn.textContent = originalText; + }, 2000); + }); + }); + titleBar.appendChild(copyBtn); + + // 组装弹窗 + popup.appendChild(titleBar); + popup.appendChild(tabContainer); + popup.appendChild(contentContainer); + document.body.appendChild(popup); + + // 点击外部关闭 + popup.addEventListener('click', (e) => { + if (e.target === popup) { + popup.remove(); + } + }); + }; + + // 收集页面信息 + const collectPageInfo = () => { + const info = {}; + + // 1. 概览信息 + info.overview = ''; + info.overview += '='.repeat(60) + ' 页面概览 ' + '='.repeat(60) + '\n\n'; + info.overview += `检查时间: ${new Date().toLocaleString()}\n`; + info.overview += `页面URL: ${window.location.href}\n`; + info.overview += `域名: ${window.location.hostname}\n`; + info.overview += `协议: ${window.location.protocol}\n`; + info.overview += `端口: ${window.location.port || '默认'}\n`; + info.overview += `路径: ${window.location.pathname}\n`; + info.overview += `查询参数: ${window.location.search || '无'}\n`; + info.overview += `哈希: ${window.location.hash || '无'}\n\n`; + info.overview += `页面标题: ${document.title}\n`; + info.overview += `字符编码: ${document.characterSet || document.charset}\n`; + info.overview += `文档类型: ${document.doctype ? document.doctype.name : '未知'}\n`; + info.overview += `语言: ${document.documentElement.lang || '未设置'}\n\n`; + info.overview += `视口尺寸: ${window.innerWidth}×${window.innerHeight}\n`; + info.overview += `设备像素比: ${window.devicePixelRatio}\n`; + info.overview += `用户代理: ${navigator.userAgent.substring(0, 100)}...\n`; + info.overview += `平台: ${navigator.platform}\n`; + info.overview += `在线状态: ${navigator.onLine ? '在线' : '离线'}\n`; + + // 2. Cookie信息 + try { + const cookies = document.cookie; + info.overview += `\nCookie数量: ${cookies ? cookies.split(';').length : 0}\n`; + if (cookies) { + info.overview += `Cookie内容:\n`; + cookies.split(';').forEach((cookie, i) => { + info.overview += ` ${i + 1}. ${cookie.trim()}\n`; + }); + } + } catch (e) { + info.overview += `\nCookie访问被阻止\n`; + } + + // 3. 元素检查 + info.elements = ''; + info.elements += '='.repeat(60) + ' 元素检查 ' + '='.repeat(60) + '\n\n'; + + const elementsToCheck = [ + // 常用容器 + '#content', '#main', '#container', '#wrapper', '#app', + // 图片相关 + '#basicExample', '#gallery', '.gallery', '#images', '.images', + '#photos', '.photos', '#pictures', '.pictures', + '#image-container', '.image-container', '#img-container', '.img-container', + '#photo-container', '.photo-container', + // 内容相关 + '#article', '.article', '#post', '.post', '#entry', '.entry', + '#blog', '.blog', '#news', '.news', + // 布局相关 + '#body', '#page', '#site', '#wrap', '#frame' + ]; + + const foundElements = []; + elementsToCheck.forEach(selector => { + try { + const element = document.querySelector(selector); + if (element) { + const imgCount = element.querySelectorAll('img').length; + const linkCount = element.querySelectorAll('a').length; + foundElements.push({ + selector, + tagName: element.tagName, + className: element.className || '无', + id: element.id || '无', + dimensions: `${element.offsetWidth}×${element.offsetHeight}`, + imgCount, + linkCount, + htmlPreview: element.outerHTML.substring(0, 150) + '...' + }); + } + } catch (e) { + // 忽略错误 + } + }); + + if (foundElements.length > 0) { + info.elements += `找到 ${foundElements.length} 个相关元素:\n\n`; + foundElements.forEach((elem, i) => { + info.elements += `[${i + 1}] ${elem.selector}\n`; + info.elements += ` 元素类型: ${elem.tagName}\n`; + info.elements += ` 尺寸: ${elem.dimensions}\n`; + info.elements += ` class/id: ${elem.className} / ${elem.id}\n`; + info.elements += ` 包含图片: ${elem.imgCount} 张\n`; + info.elements += ` 包含链接: ${elem.linkCount} 个\n`; + info.elements += ` 代码预览: ${elem.htmlPreview}\n\n`; + }); + } else { + info.elements += '未找到常见容器元素\n'; + } + + // 4. 图片分析 + info.images = ''; + info.images += '='.repeat(60) + ' 图片分析 ' + '='.repeat(60) + '\n\n'; + + const allImages = document.querySelectorAll('img'); + const imageInfo = []; + + allImages.forEach((img, index) => { + try { + const rect = img.getBoundingClientRect(); + const parent = img.parentElement; + imageInfo.push({ + index: index + 1, + src: img.src || img.dataset.src || img.currentSrc || '', + alt: img.alt || '无', + title: img.title || '无', + className: img.className || '无', + id: img.id || '无', + loading: img.loading || 'auto', + complete: img.complete, + naturalSize: img.naturalWidth > 0 ? `${img.naturalWidth}×${img.naturalHeight}` : '未加载', + displaySize: rect.width > 0 ? `${Math.round(rect.width)}×${Math.round(rect.height)}` : '不可见', + parent: `${parent.tagName}${parent.id ? '#' + parent.id : ''}${parent.className ? '.' + parent.className.replace(/\s+/g, '.') : ''}`, + visible: rect.width > 0 && rect.height > 0 + }); + } catch (e) { + // 忽略错误 + } + }); + + info.images += `总图片数: ${allImages.length}\n`; + info.images += `可见图片: ${imageInfo.filter(img => img.visible).length}\n`; + info.images += `已加载完成: ${imageInfo.filter(img => img.complete).length}\n\n`; + + info.images += `图片统计 (前20张):\n`; + imageInfo.slice(0, 20).forEach(img => { + info.images += `\n[${img.index}] `; + if (img.src) { + const url = new URL(img.src, window.location.href); + const shortSrc = url.hostname === window.location.hostname ? + url.pathname.substring(0, 40) : + url.hostname + url.pathname.substring(0, 20); + info.images += `${shortSrc}${img.src.length > 60 ? '...' : ''}\n`; + } else { + info.images += '无src\n'; + } + info.images += ` alt/title: ${img.alt} / ${img.title}\n`; + info.images += ` class/id: ${img.className} / ${img.id}\n`; + info.images += ` 加载状态: ${img.loading} (${img.complete ? '已完成' : '未完成'})\n`; + info.images += ` 原始尺寸: ${img.naturalSize}\n`; + info.images += ` 显示尺寸: ${img.displaySize}\n`; + info.images += ` 父元素: ${img.parent}\n`; + info.images += ` 是否可见: ${img.visible ? '✅' : '❌'}\n`; + }); + + // 5. 链接分析 + info.links = ''; + info.links += '='.repeat(60) + ' 链接分析 ' + '='.repeat(60) + '\n\n'; + + const allLinks = document.querySelectorAll('a'); + const linkInfo = []; + + allLinks.forEach((link, index) => { + try { + linkInfo.push({ + index: index + 1, + href: link.href || '', + text: link.textContent.trim().substring(0, 50) || '无文本', + title: link.title || '无', + target: link.target || '_self', + rel: link.rel || '无', + download: link.download || '否', + isExternal: link.hostname && link.hostname !== window.location.hostname, + isAnchor: link.hash && !link.hostname + }); + } catch (e) { + // 忽略错误 + } + }); + + info.links += `总链接数: ${allLinks.length}\n`; + info.links += `外部链接: ${linkInfo.filter(link => link.isExternal).length}\n`; + info.links += `锚点链接: ${linkInfo.filter(link => link.isAnchor).length}\n`; + info.links += `下载链接: ${linkInfo.filter(link => link.download !== '否').length}\n\n`; + + info.links += `链接列表 (前15个):\n`; + linkInfo.slice(0, 15).forEach(link => { + info.links += `\n[${link.index}] ${link.text}\n`; + if (link.href) { + const url = new URL(link.href, window.location.href); + const displayHref = url.hostname === window.location.hostname ? + url.pathname + url.search : + url.hostname + url.pathname; + info.links += ` 地址: ${displayHref.substring(0, 80)}${link.href.length > 80 ? '...' : ''}\n`; + } + info.links += ` title: ${link.title}\n`; + info.links += ` target: ${link.target}\n`; + info.links += ` rel: ${link.rel}\n`; + info.links += ` 下载: ${link.download}\n`; + info.links += ` 类型: ${link.isExternal ? '外部' : link.isAnchor ? '锚点' : '内部'}\n`; + }); + + // 6. 存储信息 + info.storage = ''; + info.storage += '='.repeat(60) + ' 存储信息 ' + '='.repeat(60) + '\n\n'; + + try { + // LocalStorage + info.storage += 'LocalStorage:\n'; + if (localStorage.length > 0) { + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + const value = localStorage.getItem(key); + info.storage += ` ${i + 1}. ${key}: ${value.substring(0, 100)}${value.length > 100 ? '...' : ''}\n`; + } + } else { + info.storage += ' 无数据\n'; + } + + // SessionStorage + info.storage += '\nSessionStorage:\n'; + if (sessionStorage.length > 0) { + for (let i = 0; i < sessionStorage.length; i++) { + const key = sessionStorage.key(i); + const value = sessionStorage.getItem(key); + info.storage += ` ${i + 1}. ${key}: ${value.substring(0, 100)}${value.length > 100 ? '...' : ''}\n`; + } + } else { + info.storage += ' 无数据\n'; + } + } catch (e) { + info.storage += `存储访问被阻止: ${e.message}\n`; + } + + // 7. 网络信息 + info.network = ''; + info.network += '='.repeat(60) + ' 网络信息 ' + '='.repeat(60) + '\n\n'; + + info.network += `连接类型: ${navigator.connection ? (navigator.connection.effectiveType || '未知') : '未知'}\n`; + info.network += `下行速度: ${navigator.connection ? (navigator.connection.downlink || '未知') + ' Mbps' : '未知'}\n`; + info.network += `网络延迟: ${navigator.connection ? (navigator.connection.rtt || '未知') + ' ms' : '未知'}\n`; + info.network += `数据节省: ${navigator.connection ? (navigator.connection.saveData ? '是' : '否') : '未知'}\n\n`; + + // 性能信息 + info.performance = ''; + info.performance += '='.repeat(60) + ' 性能信息 ' + '='.repeat(60) + '\n\n'; + + if (window.performance && performance.timing) { + const timing = performance.timing; + info.performance += `页面加载时间: ${timing.loadEventEnd - timing.navigationStart}ms\n`; + info.performance += `DOM加载时间: ${timing.domContentLoadedEventEnd - timing.navigationStart}ms\n`; + info.performance += `白屏时间: ${timing.responseStart - timing.navigationStart}ms\n`; + info.performance += `DNS查询: ${timing.domainLookupEnd - timing.domainLookupStart}ms\n`; + info.performance += `TCP连接: ${timing.connectEnd - timing.connectStart}ms\n`; + info.performance += `请求响应: ${timing.responseEnd - timing.responseStart}ms\n`; + info.performance += `DOM解析: ${timing.domComplete - timing.domInteractive}ms\n`; + } else { + info.performance += '性能API不可用\n'; + } + + info.performance += `\n内存使用: ${performance.memory ? + `已用 ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB / 总计 ${Math.round(performance.memory.totalJSHeapSize / 1024 / 1024)}MB` : + '不可用'}\n`; + + // 8. 控制台信息 + info.console = ''; + info.console += '='.repeat(60) + ' 控制台信息 ' + '='.repeat(60) + '\n\n'; + info.console += '这是一个占位符。实际控制台信息需要重写console方法捕获。\n'; + info.console += '要查看完整控制台输出,请使用浏览器开发者工具。\n'; + + return info; + }; + + // 点击事件 + inspectBtn.addEventListener('click', function() { + const pageInfo = collectPageInfo(); + createInspectPopup(pageInfo); + }); + + // 添加到页面 + document.body.appendChild(inspectBtn); + + // 添加样式 + const style = document.createElement('style'); + style.textContent = ` + #page-inspector-btn:hover { + background-color: #0056b3 !important; + transform: scale(1.05); + transition: all 0.2s ease; + } + + #inspector-popup .tab-content::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + #inspector-popup .tab-content::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; + } + + #inspector-popup .tab-content::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; + } + + #inspector-popup .tab-content::-webkit-scrollbar-thumb:hover { + background: #555; + } + + #inspector-popup button[data-tab]:hover { + background-color: #3b5168 !important; + } + `; + document.head.appendChild(style); +})(); \ No newline at end of file