You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
ImagesDownloader/Tampermonkey/PageDebugHelper.js

522 lines
22 KiB

// ==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);
})();