无需登录 数据私有 本地保存

HTML注释提取器 - 提取页面隐藏注释

54
0
0
0

HTML 注释提取器

从 HTML 源代码中提取所有隐藏注释,支持粘贴代码或 URL 获取,快速审查页面中的开发者备注、遗留代码和隐藏信息。

部分网站可能因跨域限制无法直接获取,建议使用「粘贴代码」方式。
提取结果

粘贴 HTML 代码后点击「提取注释」
即可在此查看所有隐藏注释

常见问题与知识点

HTML 注释是网页源代码中不会被浏览器渲染显示的内容,用于开发者添加备注、说明或临时隐藏代码。其标准语法为 <!-- 注释内容 -->,可以跨越多行。浏览器解析 HTML 时会自动忽略注释标签内的所有内容,因此注释不会影响页面视觉效果,但会增加页面源代码的体积。

提取 HTML 注释有多种实用场景:安全审计——检查开发者是否在注释中意外泄露了敏感信息(如 API 密钥、数据库连接字符串、内部路径等);代码审查——查看遗留的 TODO、FIXME 标记和废弃代码片段;竞品分析——了解竞争对手网站的技术栈和开发思路;SEO 优化——清理不必要的注释以减少页面体积,提升加载速度;学习研究——通过阅读注释了解网站架构和开发逻辑。

虽然搜索引擎会忽略 HTML 注释中的内容(不会将其作为排名因素),但过多的注释会增加页面文件大小,影响加载速度,而页面加载速度是 Google 等搜索引擎的重要排名因素。此外,如果注释中包含大量关键词堆砌,某些搜索引擎可能将其视为垃圾信息。建议在生产环境中移除不必要的开发注释,使用 HTML 压缩工具精简代码。

有几种方法可以查看网页的 HTML 注释:① 查看页面源代码——在浏览器中右键点击页面空白处,选择「查看页面源代码」(快捷键 Ctrl+U / Cmd+U),在源代码中搜索 <!-- 即可找到所有注释;② 开发者工具——按 F12 打开开发者工具,在 Elements/Inspector 面板中可以展开 DOM 树查看注释节点(显示为灰色);③ 使用本工具——直接粘贴源代码或输入 URL 自动提取,更加高效便捷。

IE 条件注释(Conditional Comments)是微软在 Internet Explorer 浏览器中引入的特殊语法,格式为 <!--[if IE]>...<![endif]-->,用于为不同版本的 IE 浏览器提供特定的样式或脚本。虽然现代浏览器已不再支持条件注释,但在一些老旧网站中仍能见到。本工具会自动提取条件注释,并用 条件注释 标签进行标记,方便您识别和区分。

本工具支持两种导出格式:TXT 纯文本——将所有注释按顺序导出,每条注释之间用分隔线隔开,方便直接阅读和存档;JSON 格式——导出包含注释内容、行号、字符长度、是否为条件注释等结构化数据,便于程序化处理和分析。您也可以使用「复制全部」按钮一键将所有注释复制到剪贴板。

从 URL 获取 HTML 时可能因跨域策略(CORS)限制而失败——浏览器出于安全考虑,禁止前端直接请求不同域名下的资源。如果您需要分析自己的网站,可以在服务器端配置允许跨域访问;对于第三方网站,建议使用「查看页面源代码」的方式复制 HTML 后粘贴到本工具中进行分析,这是最可靠的提取方式。

HTML 注释使用 <!-- --> 语法,作用于 HTML 文档层面,浏览器不会渲染注释内容;JavaScript 注释使用 //(单行)或 /* */(多行)语法,作用于脚本层面,JavaScript 引擎会忽略注释。需要注意的是,在 <script> 标签内的 <!-- --> 在某些老旧浏览器中可能被当作 JavaScript 注释处理,现代浏览器已不再支持这种做法。本工具专注于提取 HTML 层面的注释。
✓ 已复制到剪贴板
`; // 初始化:加载示例到textarea function loadSample() { $htmlInput.val(sampleHtml); // 切换到粘贴标签 $('#paste-tab').tab('show'); // 自动提取 extractComments(); } // 提取注释的核心函数 function extractComments() { const html = $htmlInput.val().trim(); currentSourceHtml = html; if (!html) { showEmptyState(); return; } // 正则匹配HTML注释(包括条件注释) const commentRegex = //g; const comments = []; let match; while ((match = commentRegex.exec(html)) !== null) { const fullMatch = match[0]; const content = match[1]; const startIndex = match.index; const endIndex = startIndex + fullMatch.length; // 计算行号 const beforeText = html.substring(0, startIndex); const lineNumber = (beforeText.match(/\n/g) || []).length + 1; const afterText = html.substring(0, endIndex); const endLineNumber = (afterText.match(/\n/g) || []).length + 1; // 检测是否为条件注释 const isConditional = /^\s*\[if\s+/i.test(content); comments.push({ fullMatch: fullMatch, content: content, startIndex: startIndex, endIndex: endIndex, lineNumber: lineNumber, endLineNumber: endLineNumber, length: content.length, isConditional: isConditional, preview: content.replace(/\s+/g, ' ').trim().substring(0, 120) }); } currentComments = comments; renderResults(comments); } // 渲染结果 function renderResults(comments) { if (comments.length === 0) { // 有HTML但没有注释 if (currentSourceHtml.trim()) { $commentsList.html(`

该 HTML 代码中未发现任何注释
共扫描 ${currentSourceHtml.length.toLocaleString()} 个字符

`); $commentCount.text('0 条注释').show(); $statsRow.hide(); $searchFilter.hide(); $resultActions.hide(); } else { showEmptyState(); } return; } // 更新计数 $commentCount.text(`${comments.length} 条注释`).show(); $resultActions.show(); $searchFilter.show(); // 统计信息 const totalChars = comments.reduce((sum, c) => sum + c.length, 0); const avgLength = Math.round(totalChars / comments.length); const maxComment = comments.reduce((max, c) => c.length > max.length ? c : max, comments[0]); const minComment = comments.reduce((min, c) => c.length < min.length ? c : min, comments[0]); const conditionalCount = comments.filter(c => c.isConditional).length; let statsHtml = ` 总字符数 ${totalChars.toLocaleString()} 平均长度 ${avgLength.toLocaleString()} 字符 最长 ${maxComment.length.toLocaleString()} 字符 最短 ${minComment.length.toLocaleString()} 字符 `; if (conditionalCount > 0) { statsHtml += `条件注释 ${conditionalCount}`; } $statsRow.html(statsHtml).show(); // 渲染列表 renderCommentList(comments); // 应用当前筛选 const filterText = $filterInput.val().trim(); if (filterText) { applyFilter(filterText); } } // 渲染注释列表 function renderCommentList(comments) { if (comments.length === 0) { $commentsList.html(`

没有匹配的注释

`); return; } let html = ''; comments.forEach((c, i) => { const escapedContent = escapeHtml(c.content); const escapedPreview = escapeHtml(c.preview || c.content.replace(/\s+/g, ' ').trim().substring(0, 120)); const displayIndex = i + 1; const lineInfo = c.lineNumber === c.endLineNumber ? `第 ${c.lineNumber} 行` : `第 ${c.lineNumber}-${c.endLineNumber} 行`; html += `
${displayIndex} ${lineInfo} ${c.length.toLocaleString()} 字符 ${c.isConditional ? '条件注释' : ''}
${escapedPreview}
`; }); $commentsList.html(html); // 点击展开/收起 $commentsList.find('.comment-item').on('click', function(e) { if ($(e.target).closest('.btn-copy-single').length) return; $(this).toggleClass('expanded'); }); // 单条复制 $commentsList.find('.btn-copy-single').on('click', function(e) { e.stopPropagation(); const idx = $(this).data('copy-index'); if (currentComments[idx]) { copyToClipboard(currentComments[idx].content, '已复制该条注释'); } }); } // 筛选过滤 function applyFilter(filterText) { const lowerFilter = filterText.toLowerCase(); let filtered; if (lowerFilter) { filtered = currentComments.filter(c => c.content.toLowerCase().includes(lowerFilter) ); } else { filtered = currentComments; } renderCommentList(filtered); // 高亮匹配文本 if (lowerFilter && filtered.length > 0) { $commentsList.find('.comment-preview').each(function() { const $this = $(this); const text = $this.text(); const regex = new RegExp(`(${escapeRegex(lowerFilter)})`, 'gi'); $this.html(escapeHtml(text).replace(regex, '$1')); }); } } // 显示空状态 function showEmptyState() { $commentsList.html(`

粘贴 HTML 代码后点击「提取注释」
即可在此查看所有隐藏注释

`); $commentCount.hide(); $statsRow.hide(); $searchFilter.hide(); $resultActions.hide(); currentComments = []; } // 复制到剪贴板 function copyToClipboard(text, message) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(() => showToast(message || '已复制到剪贴板')); } else { // Fallback const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); showToast(message || '已复制到剪贴板'); } catch (e) { showToast('复制失败,请手动选择复制'); } document.body.removeChild(textarea); } } // Toast提示 function showToast(message) { $toastCopy.text(message).addClass('show'); clearTimeout($toastCopy.data('timeout')); $toastCopy.data('timeout', setTimeout(() => { $toastCopy.removeClass('show'); }, 1800)); } // HTML转义 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 属性转义(用于data属性) function escapeAttr(text) { return text.replace(/&/g, '&').replace(/"/g, '"').replace(//g, '>'); } // 正则转义 function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // 下载文件 function downloadFile(content, filename, mimeType) { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // 生成TXT内容 function generateTxtContent(comments) { let txt = ''; comments.forEach((c, i) => { txt += `========================================\n`; txt += `注释 #${i + 1} | 行号: ${c.lineNumber} | 长度: ${c.length} 字符`; if (c.isConditional) txt += ` | [条件注释]`; txt += `\n========================================\n`; txt += c.content + '\n\n'; }); txt += `\n总计: ${comments.length} 条注释\n`; txt += `导出时间: ${new Date().toLocaleString()}\n`; return txt; } // 生成JSON内容 function generateJsonContent(comments) { const data = comments.map((c, i) => ({ index: i + 1, content: c.content, lineNumber: c.lineNumber, endLineNumber: c.endLineNumber, length: c.length, isConditional: c.isConditional, startIndex: c.startIndex, endIndex: c.endIndex })); return JSON.stringify({ totalComments: comments.length, exportTime: new Date().toISOString(), comments: data }, null, 2); } // 从URL获取HTML function fetchFromUrl(url) { $fetchStatus.show().html(`
正在获取 HTML...
`); $btnFetchUrl.prop('disabled', true); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 20000); fetch(url, { signal: controller.signal, headers: { 'Accept': 'text/html' } }) .then(response => { clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.text(); }) .then(html => { $fetchStatus.html(` 获取成功!已自动填充并提取注释 `); $htmlInput.val(html); $('#paste-tab').tab('show'); extractComments(); }) .catch(error => { clearTimeout(timeoutId); let errorMsg = ''; if (error.name === 'AbortError') { errorMsg = '请求超时(超过20秒),请检查URL或使用粘贴方式'; } else if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) { errorMsg = '网络请求失败,可能是跨域(CORS)限制。建议在浏览器中打开该页面,右键「查看页面源代码」,复制后粘贴到工具中'; } else { errorMsg = `获取失败:${error.message}`; } $fetchStatus.html(` ${errorMsg} `); }) .finally(() => { $btnFetchUrl.prop('disabled', false); }); } // 事件绑定 $btnExtract.on('click', extractComments); $btnLoadSample.on('click', loadSample); $btnClear.on('click', function() { $htmlInput.val(''); $urlInput.val(''); $filterInput.val(''); $fetchStatus.hide(); showEmptyState(); }); $btnFetchUrl.on('click', function() { const url = $urlInput.val().trim(); if (!url) { $fetchStatus.show().html(` 请输入有效的URL地址 `); return; } // 简单URL验证 try { new URL(url); } catch (e) { $fetchStatus.show().html(` URL格式不正确,请输入完整地址(包含 https://) `); return; } fetchFromUrl(url); }); // 筛选输入实时过滤 let filterDebounce; $filterInput.on('input', function() { clearTimeout(filterDebounce); const filterText = $(this).val().trim(); filterDebounce = setTimeout(() => { applyFilter(filterText); }, 250); }); // 复制全部 $btnCopyAll.on('click', function() { if (currentComments.length === 0) return; const allContent = currentComments.map((c, i) => `[注释 ${i + 1}]\n${c.content}`).join('\n\n---\n\n'); copyToClipboard(allContent, `已复制 ${currentComments.length} 条注释`); }); // 下载TXT $btnDownloadTxt.on('click', function() { if (currentComments.length === 0) return; const txt = generateTxtContent(currentComments); downloadFile(txt, 'html-comments.txt', 'text/plain;charset=utf-8'); showToast('TXT 文件下载中...'); }); // 下载JSON $btnDownloadJson.on('click', function() { if (currentComments.length === 0) return; const json = generateJsonContent(currentComments); downloadFile(json, 'html-comments.json', 'application/json;charset=utf-8'); showToast('JSON 文件下载中...'); }); // 键盘快捷键 Ctrl+Enter 提取注释 $htmlInput.on('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); extractComments(); } }); // URL输入框回车触发获取 $urlInput.on('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); $btnFetchUrl.trigger('click'); } }); // Tab切换时隐藏URL获取状态 $('#paste-tab').on('shown.bs.tab', function() { $fetchStatus.hide(); }); // 初始化:预加载示例(让用户看到效果) setTimeout(() => { if (!$htmlInput.val().trim()) { loadSample(); } }, 300); });