基于TypeScript开发SEO审计工具:孤立页面检测、类PageRank链接权益计算及主题关联链接建议实现需求
实现SEO审计工具的核心功能方案
针对你开发SEO审计工具时遇到的三个核心需求,以及现有手动方案的痛点,我整理了一套可落地的实现思路,结合你期望的API设计来拆解:
一、先解决URL标准化,构建靠谱的有向链接图
URL标准化是所有链接分析的基础,之前的匹配遗漏问题大多源于此。先写一个工具函数统一处理URL:
function normalizeUrl(url) { const urlObj = new URL(url); // 统一去掉末尾斜杠 urlObj.pathname = urlObj.pathname.replace(/\/$/, ''); // 忽略大小写、统一协议(按需调整) return urlObj.href.toLowerCase(); }
接着实现buildLinkGraph,帮你构建包含入站/出站计数的有向图——这是后续所有分析的核心载体:
function buildLinkGraph(pages) { const nodes = new Map(); const edges = new Map(); // 先初始化所有页面节点 pages.forEach(page => { const normalizedUrl = normalizeUrl(page.url); nodes.set(normalizedUrl, { url: page.url, normalizedUrl, inboundCount: 0, outboundCount: page.links.length, outboundLinks: page.links.map(link => normalizeUrl(link)) }); }); // 遍历统计每个页面的入站链接数 nodes.forEach(node => { node.outboundLinks.forEach(targetUrl => { if (nodes.has(targetUrl)) { nodes.get(targetUrl).inboundCount += 1; // 存储边信息,用于后续权益计算 if (!edges.has(node.normalizedUrl)) edges.set(node.normalizedUrl, []); edges.get(node.normalizedUrl).push(targetUrl); } }); }); return { nodes, edges }; }
这个图结构直接解决了你之前无法统计入站/出站链接的问题,而且性能比手动遍历高得多。
二、高效检测孤立页面
基于上面的链接图,findOrphanPages可以做到O(n)时间复杂度,再也不用纠结URL匹配的问题:
function findOrphanPages(graph) { return Array.from(graph.nodes.values()) .filter(node => node.inboundCount === 0) .map(({ url, outboundCount }) => ({ url, outboundCount })); }
只要页面的入站计数为0,就是爬虫无法发现的孤立页面,逻辑清晰又准确。
三、计算类PageRank的链接权益分数
PageRank的核心是“链接投票”,这里实现一个简化版,支持阻尼因子和迭代次数配置:
function analyzeLinkEquity(graph, { damping = 0.85, iterations = 20 }) { const nodeList = Array.from(graph.nodes.keys()); const totalNodes = nodeList.length; let scores = new Map(); // 初始化分数:每个页面初始权重均等 nodeList.forEach(url => scores.set(url, 1 / totalNodes)); for (let i = 0; i < iterations; i++) { const newScores = new Map(); const randomSurferScore = (1 - damping) / totalNodes; nodeList.forEach(nodeUrl => { let score = randomSurferScore; const edges = graph.edges.get(nodeUrl) || []; if (edges.length > 0) { // 有出站链接的页面,按比例传递权重 const share = damping * scores.get(nodeUrl) / edges.length; edges.forEach(targetUrl => { newScores.set(targetUrl, (newScores.get(targetUrl) || 0) + share); }); } else { // 无出站链接的页面,权重均匀分给所有页面 nodeList.forEach(targetUrl => { newScores.set(targetUrl, (newScores.get(targetUrl) || 0) + damping * scores.get(nodeUrl) / totalNodes); }); } }); scores = newScores; } // 转换为你期望的输出格式,按分数从高到低排序 return Array.from(scores.entries()) .map(([normalizedUrl, score]) => ({ url: graph.nodes.get(normalizedUrl).url, score: parseFloat(score.toFixed(2)), inboundCount: graph.nodes.get(normalizedUrl).inboundCount })) .sort((a, b) => b.score - a.score); }
这个函数会输出每个页面的权威度分数,帮你快速定位全站最有价值的页面。
四、基于主题重叠的链接建议
要实现这个功能,核心是计算页面间的主题相似度。这里给你一个简化版的实现,你可以后续结合专业NLP库优化:
// 辅助函数:提取页面关键词(简单版,实际可替换为TF-IDF等算法) function extractKeywords({ title, content }) { const text = `${title} ${content}`.toLowerCase(); // 基础停用词表,可按需扩展 const stopWords = ['the', 'and', 'of', 'a', 'to', 'in', 'is']; return text.match(/\w+/g) ?.filter(word => !stopWords.includes(word)) .reduce((acc, word) => { acc[word] = (acc[word] || 0) + 1; return acc; }, {}) || {}; } // 计算两个关键词集合的余弦相似度 function calculateSimilarity(keywordsA, keywordsB) { const allWords = new Set([...Object.keys(keywordsA), ...Object.keys(keywordsB)]); let dotProduct = 0; let normA = 0; let normB = 0; allWords.forEach(word => { const a = keywordsA[word] || 0; const b = keywordsB[word] || 0; dotProduct += a * b; normA += a * a; normB += b * b; }); if (normA === 0 || normB === 0) return 0; return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } function suggestLinks(pages, { minRelevance = 0.15, maxSuggestions = 3 }) { // 先给每个页面提取关键词 const pageData = pages.map(page => ({ ...page, keywords: extractKeywords(page) })); const suggestions = []; // 两两对比页面,计算相似度 for (let i = 0; i < pageData.length; i++) { const sourcePage = pageData[i]; for (let j = i + 1; j < pageData.length; j++) { const targetPage = pageData[j]; const similarity = calculateSimilarity(sourcePage.keywords, targetPage.keywords); if (similarity >= minRelevance) { suggestions.push({ sourceUrl: sourcePage.url, targetUrl: targetPage.url, relevance: parseFloat(similarity.toFixed(3)) }); } } } // 按相关性排序,取前N个建议 return suggestions.sort((a, b) => b.relevance - a.relevance).slice(0, maxSuggestions); }
这个方案会根据页面标题、内容的主题重叠度,生成上下文相关的链接建议,帮你优化网站内部链接结构。
内容的提问来源于stack exchange,提问作者Al Amin
相关产品推荐
相关产品推荐

