GEO 友好度检测器技术实现

面向 AI 搜索引擎优化(GEO)的纯前端静态检测工具,从 4 个维度、100 分制自动评估网页的 GEO 友好度,生成可落地的优化清单。所有分析在浏览器本地完成,零数据上传。


一、为什么需要 GEO 检测器

传统 SEO 关注关键词排名与爬虫抓取,而 GEO(Generative Engine Optimization)面向的是豆包、Kimi、文心一言、ChatGPT 等大模型。AI 不会"浏览"网页,它会"读取"结构化数据。你的页面有没有 JSON-LD、Meta 是否完整、内容语义是否清晰,直接决定了被 AI 引用的概率。

本文实现的检测器要解决的核心问题:让站长/开发者用 10 秒钟,快速自检页面的 GEO 健康度


二、检测维度设计:4 个维度,100 分制

检测器将 GEO 友好度拆解为 4 个可量化维度,总分 100:

维度 分值 核心检测项
结构化数据 30 JSON-LD 数量与类型、语法有效性、Microdata/RDFa
Meta 标签 25 title/description、OpenGraph、Twitter Card、canonical
内容语义 25 H1 唯一性、段落长度、列表/引用/图片 alt
AI 可读性 20 html lang、正文长度、FAQPage、HowTo Schema

2.1 结构化数据检测(30 分)

AI 搜索引擎最依赖的就是 Schema.org 结构化数据。检测器会扫描页面中所有 application/ld+json 脚本块:

function checkStructuredData(doc) {
  const jsonLd = Array.from(doc.querySelectorAll('script[type="application/ld+json"]'));
  const types = new Set();
  let validCount = 0;
  jsonLd.forEach((s) => {
    try {
      const data = JSON.parse(s.textContent.trim());
      collectTypes(data, types);  // 递归收集 @type
      validCount++;
    } catch (e) {
      // 解析失败不计为有效
    }
  });
  const microdata = doc.querySelectorAll("[itemscope]").length;
  return { jsonLdCount: jsonLd.length, types: Array.from(types), hasMicrodata: microdata > 0, validCount };
}

评分细则

  • 有 JSON-LD → +10
  • JSON-LD >= 3 个(覆盖多类型)→ +10
  • 有 Microdata/RDFa → +5
  • 全部 JSON-LD 语法有效 → +5

2.2 Meta 标签检测(25 分)

Meta 信息是 AI 理解页面主题的第一入口:

function checkMeta(doc) {
  const title = (doc.querySelector("title")?.textContent || "").trim();
  const desc = (doc.querySelector('meta[name="description"]')?.getAttribute("content") || "").trim();
  const og = {
    title: doc.querySelector('meta[property="og:title"]')?.getAttribute("content"),
    desc: doc.querySelector('meta[property="og:description"]')?.getAttribute("content"),
    image: doc.querySelector('meta[property="og:image"]')?.getAttribute("content"),
    type: doc.querySelector('meta[property="og:type"]')?.getAttribute("content"),
    url: doc.querySelector('meta[property="og:url"]')?.getAttribute("content"),
  };
  const ogCount = Object.values(og).filter(Boolean).length;
  // ... Twitter Card、canonical 同理
  return { titleLen: title.length, titleOk: title.length > 0 && title.length <= 60, descOk: desc.length > 0 && desc.length <= 160, ogCount, ogOk: ogCount >= 4, /* ... */ };
}

评分细则

  • title 合规(10-60 字符)→ +5
  • description 合规(70-160 字符)→ +5
  • keywords meta → +3
  • OpenGraph 完整(>=4/5)→ +6
  • Twitter Card(>=2/3)→ +3
  • canonical → +3

2.3 内容语义检测(25 分)

内容结构直接影响大模型的抽取效果。检测器分析 heading 层级、段落长度、列表、引用块、图片 alt:

function checkContent(doc) {
  const h1Count = doc.querySelectorAll("h1").length;
  const paragraphs = Array.from(doc.querySelectorAll("p"));
  const paraLenAvg = paragraphs.length > 0
    ? Math.round(paragraphs.reduce((s, p) => s + p.textContent.trim().length, 0) / paragraphs.length)
    : 0;
  const hasBlockquote = doc.querySelectorAll("blockquote").length > 0;
  const lists = doc.querySelectorAll("ul, ol").length;
  const imgsNoAlt = Array.from(doc.querySelectorAll("img")).filter(i => !(i.getAttribute("alt") || "").trim()).length;
  return { h1Count, paraLenAvg, hasBlockquote, lists, imgsTotal: doc.querySelectorAll("img").length, imgsNoAlt };
}

评分细则

  • H1 恰好 1 个 → +8
  • 段落平均长度 <= 200 字符 → +5
  • 有 blockquote → +4
  • 有 ul/ol 列表 → +4
  • 图片全部有 alt → +4

2.4 AI 可读性检测(20 分)

这是 GEO 区别于传统 SEO 的关键维度——页面是否为大模型"友好"

function checkAI(doc) {
  const hasLang = !!doc.documentElement.getAttribute("lang");
  const text = (doc.body?.textContent || "").trim();
  const hasFaq = /FAQPage/i.test(
    Array.from(doc.querySelectorAll('script[type="application/ld+json"]'))
      .map(s => s.textContent).join(" ")
  );
  const hasHowTo = /HowTo/i.test(/* 同上 */);
  return { hasLang, wordCount: text.length, hasFaq, hasHowTo };
}

评分细则

  • html 有 lang 属性 → +5
  • 正文 > 300 字符 → +5
  • 有 FAQPage Schema → +5
  • 有 HowTo Schema → +5

FAQPage 和 HowTo 是被 AI 引用概率最高的两种 Schema 类型。FAQ 让大模型直接抽取问答对,HowTo 让步骤类内容被结构化引用。


三、前端纯静态架构:零框架、零后端、零上传

检测器是一个纯静态单页应用,技术栈极简:

tools/geo-checker/index.html   ← 页面结构与 UI
public/geo-checker.js          ← 核心检测逻辑(~790 行)
public/styles.css              ← 共享样式

3.1 为什么不用框架?

  • 工具属性:用户进来、输入、看结果,没有复杂状态流转
  • 加载速度:单 JS 文件,无打包、无依赖、首屏秒开
  • 可移植性:直接丢到任何静态托管(GitHub Pages/Cloudflare Pages/对象存储)即可运行
  • 安全透明:用户可右键"查看源代码"审计所有逻辑

3.2 HTML 解析:DOMParser

核心依赖只有浏览器原生 API——DOMParser

function parse(html) {
  const doc = new DOMParser().parseFromString(html, "text/html");
  return doc;
}

用户粘贴的 HTML 源码或代理抓取的响应文本,直接解析为 DOM,后续所有检测都用标准的 querySelectorAll 完成。

3.3 两种输入模式

模式 实现方式 数据流向
粘贴 HTML textarea + DOMParser 完全本地,零网络请求
输入 URL 自建 CORS 代理抓取 + DOMParser 仅目标 URL 经过代理,HTML 不进服务器
// URL 模式通过代理中转(用户可自配)
const PROXY_BASE = 'https://your-proxy-domain.com/proxy-path';
const proxy = PROXY_BASE + "/?url=" + encodeURIComponent(url);
const r = await fetch(proxy);
const html = await r.text();
runAnalysis(html);

隐私设计:粘贴模式下 HTML 源码全程在浏览器内存中处理,不会上传到任何服务器。


四、评分逻辑:从原始数据到维度得分

4 个维度的检测函数返回原始数据后,由对应的 calc* 函数执行评分:

function runAnalysis(html) {
  const doc = parse(html);
  const sd = checkStructuredData(doc);
  const mt = checkMeta(doc);
  const ct = checkContent(doc);
  const ai = checkAI(doc);

  const a = calcStructured(sd);  // 30 分满分
  const b = calcMeta(mt);        // 25 分满分
  const c = calcContent(ct);     // 25 分满分
  const d = calcAI(ai);          // 20 分满分

  const dims = [a, b, c, d];
  const total = dims.reduce((s, x) => s + x.score, 0);
  // ...
}

每个 calc* 函数遵循统一模式:初始化 score=0,满足条件加分,不满足则记录 issues(问题)和 sug(建议)。问题分两级:

  • bad(严重):如 title 缺失、JSON-LD 语法错误
  • warn(建议):如 keywords 缺失、段落偏长

最终按严重度排序,严重问题优先展示。


五、检测报告生成与渲染

5.1 报告结构

检测结果渲染为三部分:

  1. 总分卡片:百分制总分 + 评级标签(优秀/良好/中等/较弱)+ 评语
  2. 各维度得分:4 个进度条,直观展示短板
  3. 问题清单 + 优化建议:可操作的 TODO 列表
function render(result) {
  // 总分与评级
  const total = result.total;
  let comment = "基础可用,仍有较大优化空间。";
  let tag = "待优化";
  if (total >= 85) { comment = "表现优秀!GEO 友好度很高,被 AI 引用的概率较大。"; tag = "优秀"; }
  else if (total >= 70) { comment = "表现良好,关键维度基本达标。"; tag = "良好"; }
  else if (total >= 50) { comment = "中等水平,多个维度需要优化。"; tag = "中等"; }
  else { comment = "较弱,强烈建议按下方建议进行改造。"; tag = "较弱"; }

  // 渲染 4 个维度进度条
  result.dims.forEach((d) => {
    const ratio = Math.round((d.score / d.max) * 100);
    // ...
  });

  // 渲染问题清单与建议
  // ...
}

5.2 分享卡片:Canvas 零依赖生成

检测器支持"下载结果图片",用于社交媒体分享。实现上使用原生 Canvas 绘制,不依赖 html2canvas 等库:

function downloadShareCard({ score, tag, dims, top, wm }) {
  const canvas = document.createElement("canvas");
  const dpr = Math.min(2, window.devicePixelRatio || 1);
  canvas.width = 680 * dpr;
  canvas.height = H * dpr;
  const ctx = canvas.getContext("2d");
  ctx.scale(dpr, dpr);

  // 绘制背景、卡片、分数圆、维度进度条...
  // 最终 toBlob 触发下载
  canvas.toBlob((blob) => {
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = "geo-report-" + score + ".png";
    a.click();
  }, "image/png");
}

Canvas 绘制的好处:

  • 无外部依赖,减少 100+ KB 体积
  • 导出速度快,毫秒级
  • 样式完全可控,不受页面 CSS 影响

六、双语与扩展性设计

检测器支持中英文切换,所有文案抽取为字典对象:

const UI_STR = {
  "zh-CN": { dimStructured: "结构化数据", noJsonLd: "页面没有任何 JSON-LD 结构化数据", /* ... */ },
  en: { dimStructured: "Structured Data", noJsonLd: "No JSON-LD structured data found on page", /* ... */ }
};

语言切换时,若已有分析结果,会重新运行分析以确保问题描述和建议文本同步更新:

window.addEventListener("geo:langchange", function () {
  const htmlVal = document.querySelector("#htmlInput").value;
  if (htmlVal.trim()) runAnalysis(htmlVal);
});

七、开源与部署

检测器采用纯静态架构,代码托管于 GitHub:

部署方式非常简单:拉取代码后,将 tools/geo-checker 目录与 public 目录上传到任意静态托管平台(GitHub Pages、Cloudflare Pages、对象存储 CDN 等)即可运行,无需任何后端服务。

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐