mcpField
N
no-outlinks-audit

no-outlinks-audit

github.com/infrasity-labs营销与增长150129 次安装17 周前更新
代码仓库

关于 no-outlinks-audit

no-outlinks-audit 是用于查找博客或内容页面内部链接死角的 SEO 审计 Skill。它按页面框架检测站内出链,并为每个死角页面提供相关目标页面、锚文本和可直接使用的链接文案建议。

主要功能

  1. 01从站点地图发现内容页面
  2. 02按框架检测页面内链
  3. 03验证死角页面识别结果
  4. 04借助 Ahrefs 研究页面关键词
  5. 05为死角页生成三条内链建议

适用场景

  1. 01查找没有站内出链的博客页面
  2. 02为内容页面规划相关内链
  3. 03获取带锚文本的链接位置与文案建议

MCP 相关

该 Skill 使用 Ahrefs MCP 的 site-explorer-top-pages 工具获取页面关键词与流量数据。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/infrasity-labs/dev-gtm-claude-skills --skill no-outlinks-audit

安装后在 Claude Code 中输入/no-outlinks-audit确认已加载。

附带文件README.mdreferences/report-style-reference.html

详细介绍

技能:死胡同页面 — 无站内出链审计

描述

找出网站上正文内容中没有任何指向同一域名下 URL 的出链的所有博客/内容页面。这些页面吸收链接权重,却不传递任何权重——它们是结构性死胡同,会悄然削弱主题权威性,并降低整个网站的抓取效率。

对于每个死胡同页面,该技能会生成 3 条建议:同一网站上死胡同页面应该链接到的页面,包括准确的锚文本(目标页面的首要关键词)、在死胡同页面中放置链接的位置,以及一条可直接粘贴、已嵌入链接的上下文句子。

此技能与孤立页面技能在结构上互为反向:

  • 孤立页面:没有入站站内链接的页面(没有人链接到它们)
  • 死胡同页面:没有出站站内链接的页面(它们没有链接到任何页面)

工具

  • 第 1 步使用 bash_tool 中的 curl 发现站点地图
  • 第 2 步使用 bash_tool 中的 curl 检测框架
  • 第 3 步使用 bash_tool 中的 curl 检测出站链接(方法因框架而异)
  • 第 5 步使用 Ahrefs MCP(site-explorer-top-pages)进行关键词研究

工作流

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第 1 步 — 发现所有页面 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

为何需要此步骤 你需要一份完整的博客/内容页面清单,以便检查。基于 curl 的站点地图发现速度快、免费,而且反映的是网站的当前状态,而不是过时的抓取缓存。

子步骤 1A — 查找站点地图

按顺序运行,直到有一个返回有效的 XML:

curl -sL --max-time 15 "https://www.DOMAIN.com/sitemap.xml" | head -50
curl -sL --max-time 15 "https://www.DOMAIN.com/sitemap_index.xml" | head -50
curl -sL --max-time 15 "https://www.DOMAIN.com/robots.txt" | grep -i sitemap

如果都不行,请直接抓取博客索引:

curl -sL "https://www.DOMAIN.com/blog/" | grep -oP 'href="[^"]*"' | grep '/blog/'

子步骤 1B — 提取所有内容 URL

curl -sL "https://www.DOMAIN.com/sitemap.xml" \
  | grep -oP '(?<=<loc>)[^<]+' \
  | grep '/blog/' \
  | grep -v '/blog/$' \
  | grep -v '/page/' \
  | sort -u > /tmp/blog_urls.txt

wc -l /tmp/blog_urls.txt

将此列表称为 LIST_ALL。记录 TOTAL_PAGES = LIST_ALL 的长度。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第 2 步 — 检测网站框架 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

为何需要此步骤 出站链接的检测方法完全取决于网站渲染 HTML 的方式。使用错误的方法会导致结果完全错误——此步骤可以避免这种情况。

运行两项检查:

# Check response headers
curl -sI "https://www.DOMAIN.com" | grep -i "x-powered-by\|x-nextjs\|x-vercel\|server\|generator"

# Check HTML source for framework signals
curl -sL "https://www.DOMAIN.com" \
  | grep -o 'name="generator"[^>]*\|__NEXT_DATA__\|_next\|__nuxt\|ng-version\|gatsby'

判定表:

发现的信号框架方法
响应头中包含 x-nextjs-prerenderNext.js第 3A 步
HTML 中包含 _next 或 NEXT_DATANext.js第 3A 步
响应头中包含 x-vercel-cache 且包含 _nextNext.js/Vercel第 3A 步
HTML 源码中包含 gatsbyGatsby (SSG)第 3B 步
name="generator" content="WordPress"WordPress第 3B 步
x-powered-by: PHP传统 CMS第 3B 步
静态 HTML(存在 标签)静态/SSG第 3B 步
curl 响应中没有 标签未知 SPA第 3C 步
HTML 中包含 __nuxtNuxt.js第 3C 步

重要:运行完整列表前,务必先在 3 个示例页面上测试检测方法。确认一个已知含有链接的页面会返回链接,并且一个已知内容稀少的页面返回的链接很少或没有。如果结果看起来不对,请重新阅读此步骤。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第 3 步 — 检测每个页面的出站站内链接 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

运行第 2 步确定的方法。

──────────────────────────────────────── 第 3A 步 — NEXT.JS 网站(RSC 响应头方法) ────────────────────────────────────────

工作原理 使用 React Server Components 的 Next.js 会公开一个服务器渲染的负载端点。通过发送 RSC: 1 请求头,服务器会返回完整的渲染页面数据,序列化为 React 组件树,其中包括所有链接,无需使用无头浏览器。此方法适用于所有使用 App Router 的 Next.js 网站(Next.js 13+)。 对于任何运行在 Next.js / Vercel 上的网站,无论使用 ISR、SSG 还是 SSR,这都是正确的方法。RSC 载荷始终包含已渲染的内容,其中所有内部链接都以纯文本路径的形式显示。

如何验证抓取成功 有效的 RSC 响应:

  • 始终大于 10KB(大多数博客文章为 50KB–250KB)
  • 以 $Sreact.fragment、J: 或 1:"$ 等 React 标记开头
  • 如果页面返回的数据少于 10KB,或前 500 个字符中没有 $,则抓取失败——将该页面标记为需人工审核,切勿将其标记为无出站链接页面
import subprocess, re, time

domain = "DOMAIN.com"        # without www, e.g. "infrasity.com"
blog_prefix = "/blog/"       # content prefix, e.g. "/blog/"

with open('/tmp/blog_urls.txt') as f:
    urls = [u.strip() for u in f if u.strip()]

# Asset extensions to exclude — these are not outgoing content links
SKIP_EXT = (
    '.png','.webp','.jpg','.jpeg','.gif','.svg','.ico',
    '.woff2','.woff','.ttf','.eot','.otf',
    '.css','.js','.jsx','.ts','.tsx',
    '.xml','.txt','.pdf','.zip','.mp4','.mp3','.webm'
)

# Path prefixes to exclude — assets, CDN, internal Next.js paths
SKIP_PREFIX = (
    '/PostImages/', '/images/', '/fonts/', '/favicon',
    '/_next/', '/_vercel/', '/static/',
    '/api/', '//cdn.', '//assets.'
)

def get_outgoing_links(url):
    result = subprocess.run([
        'curl', '-sL', '--max-time', '10',
        '-H', 'RSC: 1',
        '-H', 'Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D',
        url
    ], capture_output=True, text=True, timeout=15)

    data = result.stdout

    # Validate RSC fetch succeeded
    if len(data) < 10000 or not any(m in data[:500] for m in ['$S', 'J:', '1:"']):
        return None  # Fetch failed — flag for manual review

    # Extract all paths pointing to the same domain
    # Extract both absolute domain links and relative paths starting with /
    # (excluding protocol-relative links starting with //)
    raw = re.findall(
        rf'(?:(?:https?://)?(?:www\\.)?{re.escape(domain)}|(?<!/))/(?!/)([^\\s\"\\\'\\\\),\\]]+)',
        data
    )
    raw = ['/' + r for r in raw]

    clean = set()
    for l in raw:
        # Normalise: strip trailing junk
        l = l.split('\\')[0].split('"')[0].split("'")[0].rstrip('),.]\\/')
        if len(l) < 2:
            continue
        # Exclude assets
        if any(l.lower().endswith(e) for e in SKIP_EXT):
            continue
        if any(l.startswith(p) for p in SKIP_PREFIX):
            continue
        # Exclude self-link
        slug = url.rstrip('/').split('/')[-1]
        if l.rstrip('/') == blog_prefix.rstrip('/') + '/' + slug:
            continue
        clean.add(l)

    return clean

dead_ends = []
has_outlinks = []
failed = []

for i, url in enumerate(urls):
    slug = url.split('/')[-1]
    try:
        links = get_outgoing_links(url)
        if links is None:
            failed.append(url)
            print(f"[{i+1:3d}] FAILED   {slug}")
        elif len(links) == 0:
            dead_ends.append(url)
            print(f"[{i+1:3d}] DEAD-END {slug}")
        else:
            has_outlinks.append(url)
            print(f"[{i+1:3d}] LINKS({len(links):2d}) {slug}")
    except Exception as e:
        failed.append(url)
        print(f"[{i+1:3d}] ERROR    {slug}: {e}")
    time.sleep(0.15)

with open('/tmp/dead_end_urls.txt', 'w') as f:
    f.write('\n'.join(dead_ends))

print(f"\nTotal:         {len(urls)}")
print(f"Dead-ends:     {len(dead_ends)}")
print(f"Has outlinks:  {len(has_outlinks)}")
print(f"Failed:        {len(failed)} (verify manually)")
print(f"Dead-end rate: {round(len(dead_ends)/len(urls)*100)}%")

──────────────────────────────────────── 步骤 3B — 静态网站 / WORDPRESS / 传统 CMS(标准 CURL) ────────────────────────────────────────

工作原理 对于直接提供完整渲染 HTML 的网站(静态文件、WordPress、Gatsby、传统 CMS),标准 curl 会抓取包含所有链接的完整页面。仅解析主要内容区域,以排除导航栏、页脚和侧边栏中的链接。

import subprocess, re, time
from urllib.parse import urljoin

domain = "DOMAIN.com"
base = "https://www." + domain

with open('/tmp/blog_urls.txt') as f:
    urls = [u.strip() for u in f if u.strip()]

SKIP_EXT = (
    '.png','.webp','.jpg','.jpeg','.gif','.svg','.ico',
    '.woff2','.css','.js','.xml','.pdf','.zip'
)

def get_outgoing_links(url):
    result = subprocess.run([
        'curl', '-sL', '--max-time', '10',
        '-A', 'Mozilla/5.0 (compatible; InternalLinkBot/1.0)',
        url
    ], capture_output=True, text=True, timeout=15)

    html = result.stdout

    # Extract main content area only
    content = ''
    for tag in ['article', 'main', 'body']:
        m = re.search(rf'<{tag}[^>]*>(.*?)</{tag}>', html, re.DOTALL | re.IGNORECASE)
        if m:
            content = m.group(1)
            break
    if not content:
        content = html

    hrefs = re.findall(r'<a\\s+[^>]*href\\s*=\\s*["\']([^"\'\\ ]+)["\']', content, re.IGNORECASE)

    clean = set()
    for href in hrefs:
        href = href.strip()
        if not href or href.startswith('#') or href.startswith('mailto:') or href.startswith('tel:'):
            continue
        abs_url = urljoin(base, href)
        if domain not in abs_url:
            continue
        path = '/' + abs_url.split(domain, 1)[-1].lstrip('/')
        if any(path.lower().endswith(e) for e in SKIP_EXT):
            continue
        if abs_url.rstrip('/') == url.rstrip('/'):
            continue
        clean.add(path)
    return clean

dead_ends = []
has_outlinks = []

for i, url in enumerate(urls):
    slug = url.split('/')[-1]
    links = get_outgoing_links(url)
    if links:
        has_outlinks.append(url)
        print(f"[{i+1:3d}] LINKS({len(links):2d}) {slug}")
    else:
        dead_ends.append(url)
        print(f"[{i+1:3d}] DEAD-END {slug}")
    time.sleep(0.2)

with open('/tmp/dead_end_urls.txt', 'w') as f:
    f.write('\n'.join(dead_ends))

print(f"\nTotal:         {len(urls)}")
print(f"Dead-ends:     {len(dead_ends)}")
print(f"Has outlinks:  {len(has_outlinks)}")
print(f"Dead-end rate: {round(len(dead_ends)/len(urls)*100)}%")

──────────────────────────────────────── 步骤 3C — 未知框架 / 纯 SPA — 标记并告知用户 ────────────────────────────────────────

如果无法确定框架(例如 Nuxt.js、Angular、Vue SPA),或者 curl 在 3 个样本页面中返回的 <a> 标签少于 10 个,请停止并告知用户:

“此网站似乎使用了客户端渲染,无法通过标准 curl 请求检测。要准确执行此审计,需要以下其中一项:

  1. 为此域名设置 Ahrefs Site Audit 项目(该工具使用可渲染 JS 的无头浏览器)。首次爬取完成后,请分享项目 ID。
  2. 从启用了 JavaScript 渲染的 Screaming Frog 导出爬取结果,并提供内部链接 CSV 文件。
  3. 确认所用框架的确切名称,以便应用正确的检测标头。

在至少 5 个样本页面上确认检测方法有效之前,请勿继续生成报告。”

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 步骤 4 — 验证结果 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

设置此步骤的原因 检测错误不会显现出来。配置错误的资源筛选器或抓取失败,都可能悄无声息地产生错误的无出站链接页面列表。此步骤可在生成建议前发现这些问题。

继续之前,请完成以下所有操作:

  1. 从 LIST_DEAD_ENDS 中随机选取 3 个页面。在浏览器中打开每个页面,并人工确认页面确实没有指向该域名的出站链接。如果其中任何页面有链接,请重新检查检测脚本的资源筛选器和域名模式。

  2. 从 LIST_HAS_OUTLINKS 中选取 3 个页面。在浏览器中确认它们包含指向同一域名其他页面的可见链接。

  3. 如果 DEAD_END_RATE > 80%,则很可能存在问题——抓取结果可能只包含资源链接,或者域名模式可能不匹配。请先调试再继续。

  4. 如果 DEAD_END_RATE 为 0%,筛选条件可能过宽(将资源或自链接计入了结果)。请检查已知链接较少的页面检测到了哪些链接。

  5. 对于步骤 3A(Next.js):如果失败列表非空,请人工检查其中的页面。有些页面可能确实没有出站链接,另一些则可能发生了错误。最终确定列表前,请逐个重新抓取失败页面。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 步骤 5 — 关键词研究 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

设置此步骤的原因 要生成相关的出站链接建议,需要了解每个无出站链接页面以及每个潜在目标页面的主题。一次 Ahrefs 调用即可覆盖整个博客。

工具:Ahrefs:site-explorer-top-pages

参数:

  • target:博客前缀(例如 www.DOMAIN.com/blog/)
  • mode:prefix
  • date:今天的日期 YYYY-MM-DD
  • select:url,top_keyword,sum_traffic
  • limit:500
  • order_by:sum_traffic:desc

结果:MAP_KEYWORDS — 所有页面的关键词和流量映射。

对于 MAP_KEYWORDS 中没有的任何无出站链接页面,请根据 URL slug 推导关键词: 将连字符替换为空格。切勿虚构流量数字——使用 null。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第 6 步 — 聚类并生成建议 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

为什么需要这一步 外链建议必须与无出路页面的主题相关。先进行聚类,再在集群内部及集群之间匹配,能生成自然且实用的建议。

如何聚类 根据所有无出路页面的关键词和标题,按主要主题进行分组。根据网站内容的广度创建 5–15 个集群。集群名称应根据实际内容调整,除非确有必要,否则不要使用“杂项”或“其他”等通用名称。

如何生成建议 对于每个无出路页面,从 LIST_ALL 中找出 3 个页面(网站上的任意页面,不限于其他无出路页面),这些页面应满足以下条件:

  1. 主题相关,涉及相同或相近的主题
  2. 对无出路页面的读者来说,是自然的下一步阅读内容
  3. 最好已经是有较多链接的页面,这样无出路页面就能接入更广泛的内部链接图谱

对于 3 条建议中的每一条,请提供:

锚文本 目标页面(被链接到的页面)的首要关键词。规则:

  • 使用 MAP_KEYWORDS 中目标页面的 kw 字段
  • 如果目标页面没有关键词数据,则根据其 URL slug 推导
  • 作为句子中的超链接文本时,必须读起来自然
  • 锚文本及其周围不得使用 em dash(—)

放置位置 具体说明在无出路页面的哪个章节插入外链。规则:

  • 具体到章节,不要只写“文章中的某处”
  • 符合上下文逻辑,是读者自然想要深入了解的地方
  • 示例: “在介绍核心概念的引言中” “在比较表之后,推荐后续阅读内容时” “在文章末尾的‘工具和资源’部分” “文章中段首次提及[相关主题]时”

建议上下文文案 可直接粘贴到无出路页面指定位置的句子(最多 1–2 句)。规则:

  • 必须包含锚文本,并使用指向目标页面 URL 的真实 HTML 超链接: [target_kw]
  • 放在“放置位置”所述上下文中时必须读起来自然
  • 必须提供实际价值,不能只是“点击此处了解更多”
  • 应符合无出路页面的具体语气和章节内容
  • 总计 15–35 个词(不包括 HTML 标签)
  • 文案中任何位置都不得使用 em dash(—)。请改用逗号,或重新组织句子
  • 示例: “如果你刚刚入门,清晰概述 开发者营销策略,就能为有效运用这些策略提供框架。” “将这一做法与稳健的 B2B 内容营销策略相结合的团队,会在自然流量和销售线索两方面获得复合增长。”

将此内容存储为 D[] 中每个建议对象的 copy 字段。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第 7 步 — 生成 HTML 报告 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

编写任何代码之前,请阅读: → references/report-style-reference.html

参考文件是所有 CSS 变量、排版、组件样式、间距值和 JavaScript 模式的唯一准则。不要自行设计新样式,也不要替换为其他值。请从参考文件中原样复制。

报告页眉字段

Site URL:        https://www.DOMAIN.com/blog/
Total Blogs:     TOTAL_PAGES
No Outlinks:     DEAD_END_COUNT        ← red stat (fail)
Have Outlinks:   PAGES_WITH_OUTLINKS   ← green stat (pass)
Dead-End Rate:   DEAD_END_RATE%        ← amber stat (warn)
Suggestions:     DEAD_END_COUNT × 3   ← indigo stat (accent)

修复标签(根据使用的方法选择):

  • Next.js: ⚑ RSC payload detection (Next.js) · YYYY-MM-DD
  • Static/WP: ⚑ Curl HTML parse · YYYY-MM-DD
  • Site Audit: ⚑ Ahrefs Site Audit (project ID: XXXX) · YYYY-MM-DD

报告页脚: DOMAIN.com · Dead-End Pages Audit · YYYY-MM-DD / Source: [detection method]

建议卡片标签(与孤立页面技能中的标签不同)

  • “链接至” — 目标页面(要链接到的页面)
  • “锚文本” — 目标页面的首要关键词
  • “放置位置” — 无出路页面中的章节
  • “上下文文案” — 包含嵌入链接、可直接粘贴的句子

数据结构 — D[] 数组

每个无出路页面对应一个对象:

{
  url:     "https://www.DOMAIN.com/blog/page-slug",  // dead-end page URL
  title:   "Page Title",                              // display title
  kw:      "top keyword",                             // dead-end page's own keyword
  tr:      123,                                       // monthly traffic or null
  cluster: "Cluster Name",                            // must match a key in CC{}
  s: [
    {
      from:    "Target Page Title",                   // page to link OUT TO
      fromUrl: "https://www.DOMAIN.com/blog/target",  // target URL
      anchor:  "target page keyword",                 // target's top keyword
      place:   "Where in the dead-end page to add the link",
      copy:    "Sentence with <a href=\"...\">target keyword</a> embedded."
    },
    // × 3 per dead-end page
  ]
}

字段语义说明 from 和 fromUrl 指的是目标页面(链接指向的页面),而不是来源页面。这与孤立技能的方向相反。JS 卡片构建器必须将其标记为“链接到”,而不是“来源页面”:

// In the suggestion card builder:
`<div class="sug-from-label">Link To</div>
 <div class="sug-from-title">${s.from}</div>
 <a class="sug-from-url" href="${s.fromUrl}" target="_blank">...</a>`

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 严格规则 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  1. 编写任何 HTML 或 CSS 前,先阅读 references/report-style-reference.html。 这是强制要求,没有例外。

  2. 检测任何链接前,必须始终先执行步骤 2(框架检测)。绝不要根据域名或对网站的视觉检查来猜测框架。

  3. 对于 Next.js 网站,必须始终使用 RSC 请求头方法(步骤 3A)。绝不要在 Next.js 网站上使用标准 curl,因为它会返回空的 JS 外壳并产生错误结果。

  4. 必须始终验证每个页面的 RSC 获取是否成功。响应小于 10KB 或缺少 React 标记,都意味着获取失败。标记这些页面,绝不要默默地将其标记为无出站链接页面。

  5. 绝不要将资源链接(图片、字体、脚本、样式表)计为符合条件的出站链接。仅链接到 /logo.png 和 /styles.css 的页面属于无出站链接页面。

  6. 绝不要将自链接(页面链接到自身)计为符合条件的出站链接。

  7. 生成建议前,必须始终在示例页面上验证结果(步骤 4)。如果无出站链接页面比例高于 80% 或恰好为 0%,则说明存在问题,应先调试。

  8. 锚文本 = 目标页面的首要关键词,而不是无出站链接页面的关键词。

  9. 在建议的上下文文案中,绝不要使用长破折号(—)。改用逗号或重组句子。

  10. 每个无出站链接页面必须始终恰好生成 3 条建议,不多也不少。

  11. 无出站链接页面的 URL 和全部 3 个目标页面 URL 都必须始终是可点击的 <a href> 链接,并在 target="_blank" 中打开。

  12. 绝不要偏离 report-style-reference.html 中的 CSS 值。

  13. 必须始终使用 D[] 和 CC[] 以数据驱动的方式构建报告。绝不要为单个博客卡片编写静态 HTML。

  14. 生成建议前,必须始终清理 LIST_DEAD_ENDS。移除非 200 页面、分页页面、标签/分类页面,以及任何不是独立文章的 URL。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 特殊情况 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

NEXT.JS APP ROUTER 与 PAGES ROUTER RSC 请求头适用于 App Router(Next.js 13+)。对于较旧的 Pages Router 网站,可能无法获取 RSC 负载。检测信号:如果多个页面的响应持续小于 10KB,该网站可能使用 Pages Router。可回退为在标准 HTML 响应中检查 NEXT_DATA,其中包含序列化的页面属性,也可能包含链接数据。

网站使用的 CDN 会剥离请求头 某些 CDN 配置会在自定义请求头到达源站前将其剥离。如果 RSC 响应持续小于 10KB,可通过在 URL 末尾附加缓存绕过查询参数进行测试:?_rsc=1。如果仍然失败,CDN 很可能正在剥离 RSC 请求头,应回退为使用 NEXT_DATA 方法。

大型网站(200+ 个页面) 对于博客页面超过 200 个的网站,应分批检测,每批 50 个页面,并在请求之间间隔 0.3s,以避免触发速率限制。优先处理 MAP_KEYWORDS 中流量最高的页面。如果时间有限,则审查流量最高的 100 个页面,并在报告中注明审查范围为前 100 个页面。

所有页面都没有出站链接(比例 = 100%) 这意味着检测完全失败,可能是域名模式有误,也可能是资源过滤器过于严格。运行调试检查:打印 3 个已知包含链接的页面上的原始链接。如果没有任何结果,请修复正则表达式。如果只出现资源链接,请放宽 SKIP_EXT/SKIP_PREFIX 规则。

无出站链接页面比例为 0% 所有页面似乎都有出站链接。过滤器可能过于宽松,可能将导航栏或页脚中的链接也计入了。通过将链接搜索限制在 <article> 或 <main> 标签的内容区域内,添加 is_content 过滤。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 计算最终指标 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ TOTAL_PAGES = len(LIST_ALL) DEAD_END_COUNT = len(LIST_DEAD_ENDS) PAGES_WITH_OUTLINKS = TOTAL_PAGES - DEAD_END_COUNT DEAD_END_RATE = round(DEAD_END_COUNT / TOTAL_PAGES * 100) TOTAL_SUGGESTIONS = DEAD_END_COUNT × 3

评论

支持 Markdown