GEO 友好度检测器技术实现(已开源)
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 报告结构
检测结果渲染为三部分:
- 总分卡片:百分制总分 + 评级标签(优秀/良好/中等/较弱)+ 评语
- 各维度得分:4 个进度条,直观展示短板
- 问题清单 + 优化建议:可操作的 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 等)即可运行,无需任何后端服务。
更多推荐


所有评论(0)