简介:这套GEO优化源码包围绕生成式搜索引擎优化技术展开,为需要将品牌信息推送到AI大模型首屏的开发者与技术团队,提供了可运行的代码样板和实现参考。压缩包内共3个文件,包含一个inscode工程入口、一个HTML前端页面以及一个gitignore配置项,整体6KB,结构非常紧凑。目前已有406人学习下载,适合作为快速上手GEO优化的小型范例。源码演示了AI信息投喂、关键词内容填充与大模型训练调用的基础流程,开发者可直接运行该工程,观察代码在不同输入下的响应效果,也可以按需修改后部署到自己的测试环境,用于验证GEO策略的有效性。特别是对于刚接触GEO技术的读者,这份源码能够直观展示从数据准备到模型调用的完整链路,减少自行摸索的时间。虽然包体很小,但工程链路清晰,兼顾教学演示与初步落地价值,能帮助阅读者大幅缩短从概念理解到代码验证的距离。 做了这么多年网站和内容,我越来越明确一个判断:传统的SEO打法正在被边缘化,而GEO优化才是接下来内容流量真正的增长点。这篇文章想完整分享一套我最近从零搭起来、能直接跑出结果的GEO优化与源码搭建方案,注意不是空谈方法论,而是可运行源码级别的完整工程,涵盖从内容采集、关键词分析、GEO评分到报表输出的一整个闭环。
我先把话说透:GEO这个词在不同圈子里指的东西完全不一样,有搜卫星轨道参数的,有搜游戏引擎的,但在内容运营这个场景里,GEO是Generative Engine Optimization,生成式引擎优化。它的目标不是和Google搜索排名较劲,而是让你的内容被ChatGPT、Perplexity这类AI搜索产品更好地理解、引用和推荐。这套源码就是围绕这个目标设计的,适合正在做独立站、品牌内容、SEO转GEO过渡的运营或独立开发者参考复现。
1. 项目定位:为什么GEO优化值得单独搭一套源码
1.1 AI搜索正在重塑内容分发逻辑
先看一个很直观的变化。过去用户找答案,是在搜索引擎里输入关键词,然后从十条蓝色链接里人工挑选。现在用户直接问AI助手,AI从互联网上抓取内容,经过理解和归纳后,直接给出一段整合过的答案。这意味着什么?意味着你的内容如果只是排名靠前但没有被AI引擎识别为"高质量信源",那么流量依然可能是零。
我测试过不少AI搜索产品,发现它们引用内容时有一套共同的偏好:内容结构清晰、信息密度高、实体表达明确、有可验证的数据点、有清晰的作者和发布时间。传统SEO那套"关键词密度堆砌+外链矩阵"的打法在这些引擎面前基本失效。GEO优化的本质,就是按照AI引擎的内容偏好来重新组织你的网页。
1.2 这套源码能解决什么问题
这个项目做了三件事。第一,批量拉取你自己站点(或者你有权限分析的内容)的文本,统一清洗成结构化数据。第二,从多个维度评估内容在AI引擎眼中的"可引用性",给出具体的GEO评分和改进建议。第三,把分析结果输出成可视化的报表系统,方便你追踪优化前后的变化。
我自己的使用场景是运营一个技术博客,过去六个月通过这套源码持续优化了站点里的旧文章。效果很难说精确归因,但无论是从AI搜索带过来的引荐流量,还是内容在AI问答中被提及的次数,都有肉眼可见的增长。更重要的是,这套源码让我把"凭感觉优化"变成了"按数据优化"。
2. 整体设计思路:从AI答案生成机制反推源码架构
2.1 AI搜索引擎如何"读懂"你的网页
想要做好GEO优化,你必须先理解AI引擎的回答案流程。通常分为三步:召回、理解、生成。召回阶段,AI引擎会像传统爬虫一样扫描网页内容;理解阶段,大模型会把网页内容做语义解析,提取关键实体、论点、数据;生成阶段,模型会结合用户的提问意图,从已理解的片段中挑选最匹配的内容拼装成答案。
这套流程决定了GEO源码的设计方向。你不可能控制大模型的参数,但你可以控制自己内容的"暴露面"。如果页面的核心信息散落在冗长的段落里,没有明确的标题层级,没有可独立抽取的结论句,AI引擎就很难在理解阶段高效地提取内容。所以我在源码里做了一套内容解析管线,模拟召回和理解的过程,给每篇文章打一个"GEO体检报告"。
2.2 源码的功能模块划分
整个工程我拆成了五个模块,每个模块职责单一,方便你自己裁剪或者扩展。
| 模块 | 职责 | 对应文件 |
|---|---|---|
| 内容加载器 | 拉取URL、解析正文、清洗HTML | content_loader.py |
| 关键词分析器 | 分词、实体识别、关键词权重 | keyword_analyzer.py |
| GEO评分器 | 多维度打分与改进建议 | scorer.py |
| 报表生成器 | 导出HTML/JSON格式报表 | report.py |
| Web控制台 | 本地可视化展示优化结果 | app.py |
模块之间通过标准的数据结构传递。content_loader输出清洗后的Markdown文本,keyword_analyzer从文本中提取关键词和实体,scorer综合所有信息计算出各维度得分,report负责把结果渲染成报表。这个设计有一个很明显的好处:你如果想换了采集方式,只需要替换content_loader;如果想加新的评分维度,只需要在scorer里追加一个方法。
2.3 技术选型与原因
技术栈上没有追逐新奇。Python 3.10+做底层,requests+BeautifulSoup负责采集,jieba做中文分词,Flask做Web报表,原因是这三个库的生态最成熟、遇到问题能最快找到解决方案。
我不建议在这个项目里引入重型框架,比如Scrapy或者Django。原因很简单:GEO优化的核心是分析逻辑和评分模型,不是爬虫性能。Scrapy的学习成本和部署成本远高于收益,Flask配合requests已经能应付中小站点的分析需求。完整依赖就这几行:
requests==2.31.0 beautifulsoup4==4.12.2 jieba==0.42.1 flask==3.0.0 pyyaml==6.0.13. 核心模块实现:一套可运行的GEO分析源码
3.1 内容加载器:把网页变成干净文本
这是整个流水线的第一环。网上有不少现成的正文提取库,但我实测下来,通用库对中文站点的适配性一般。这里我给出一套比较稳的简化方案:先用BeautifulSoup去掉script、style、nav、footer这些噪音节点,再抽取main或article标签作为正文主体,最后将HTML转为Markdown风格的文本。
# content_loader.py import re import requests from bs4 import BeautifulSoup class ContentLoader: def __init__(self, user_agent: str = "Mozilla/5.0 GEOBot/1.0"): self.headers = {"User-Agent": user_agent} def fetch_text(self, url: str) -> str: resp = requests.get(url, headers=self.headers, timeout=15) resp.encoding = resp.apparent_encoding soup = BeautifulSoup(resp.text, "html.parser") for tag in soup(["script", "style", "nav", "footer", "aside"]): tag.decompose() main = soup.find("main") or soup.find("article") or soup.body if not main: return "" text = main.get_text("\n", strip=True) text = re.sub(r"\n{3,}", "\n\n", text) return text一个容易踩的坑是编码问题。很多中文站点没有正确声明charset,requests默认会按ISO-8859-1解码,导致乱码。所以我把resp.encoding直接设置为apparent_encoding,这属于用一点性能换可靠性,实测下来值得。
3.2 关键词分析器:识别内容中的实体和主题
这个模块的核心是分词和关键词提取。要注意一点:GEO关键词分析和传统SEO完全不同。SEO关心的是搜索量、竞争度,GEO关心的是实体覆盖率。AI引擎回答问题时,优先选择能明确覆盖问题中实体的内容。比如用户问"北京GEO服务商哪家好",那么你的内容里如果明确出现"北京""GEO""服务商"等实体,被引用的概率就高很多。
# keyword_analyzer.py import jieba import jieba.analyse from collections import Counter class KeywordAnalyzer: def __init__(self, custom_dict: str | None = None): if custom_dict: jieba.load_userdict(custom_dict) jieba.analyse.set_stop_words("stopwords.txt") def extract_entities(self, text: str, top_k: int = 20) -> dict: keywords = jieba.analyse.extract_tags(text, topK=top_k, withWeight=True) return {kw: round(weight, 4) for kw, weight in keywords} def count_question_words(self, text: str) -> int: q_words = ["什么", "怎么", "如何", "为什么", "哪些", "哪家", "多少"] cnt = 0 for line in text.split("\n"): for qw in q_words: cnt += line.count(qw) return cnt在这里我必须单独提一句:不要忽略自定义词典。我最初跑GEO分析时,发现"GEO优化""大语言模型"这类词被jieb拆成了"GEO""优化""大语言""模型",导致关键词权重完全失真。后来我在自定义词典里加入了业务相关词条,效果立竿见影。
3.3 GEO评分器:从五个维度量化内容质量
这是整个源码最有价值的部分。我在设计时反复研究了AI引擎倾向引用的内容特征,最终确定了五个维度:结构清晰度、实体覆盖度、数据支撑度、FAQ友好度、信源可信度。每个维度0到100分,总分加权计算。
# scorer.py import re class GEOAnalyzer: WEIGHTS = { "structure": 0.25, "entity": 0.20, "data": 0.20, "faq": 0.15, "trust": 0.20, } def __init__(self, title: str, text: str, entities: dict, author: str = ""): self.title = title self.text = text self.entities = entities self.author = author def score_structure(self) -> float: headings = len(re.findall(r"^#{1,3} ", self.text, re.M)) avg_len = len(self.text) / max(len(self.text.split("\n")), 1) if headings < 3: return 45.0 if avg_len > 120: return 60.0 return 85.0 def score_entity(self) -> float: if not self.entities: return 0.0 norm = min(len(self.entities) / 15, 1.0) return round(norm * 100, 2) def score_data(self) -> float: digit_hits = len(re.findall(r"\d+%|\d+年|\d+万|\d+亿|\d+人", self.text)) return min(100, 30 + digit_hits * 10) def score_faq(self) -> float: q_hits = self.text.count("?") + self.text.count("?") return min(100, 20 + q_hits * 8) def score_trust(self) -> float: has_author = bool(self.author.strip()) has_time = bool(re.search(r"20\d{2}[-/年]", self.text)) has_cite = self.text.count("来源") + self.text.count("参考文献") return min(100, 25 + (has_author * 20) + (has_time * 20) + has_cite * 10) def overall_score(self) -> dict: scores = { "structure": self.score_structure(), "entity": self.score_entity(), "data": self.score_data(), "faq": self.score_faq(), "trust": self.score_trust(), } total = sum(scores[k] * self.WEIGHTS[k] for k in self.WEIGHTS) return {"scores": scores, "overall": round(total, 2)}评分逻辑本身不复杂,但设计权重时我费了不少心思。为什么structure占比最高?因为我实测发现,AI引擎在理解阶段严重依赖标题层级来划分内容边界。没有清晰小标题的文章,往往被引擎识别为"低信息密度",即使内容本身很好也很难被引用。
3.4 Flask报表:让优化建议肉眼可见
光有评分不够,你得知道哪篇文章差在哪里。我做了个极简的Flask Web控制台,启动后浏览器打开就能看到所有文章的GEO评分和分项得分,以及改进建议。
# app.py import json from flask import Flask, render_template_string, jsonify from scorer import GEOAnalyzer app = Flask(__name__) INDEX_HTML = """ <!DOCTYPE html> <html> <head><meta charset="utf-8"><title>GEO 报表</title></head> <body> <h1>GEO 优化报表</h1> <table border="1" cellpadding="8"> <tr><th>文章标题</th><th>总分</th><th>结构</th><th>实体</th><th>数据</th><th>FAQ</th><th>信源</th></tr> {% for item in rows %} <tr> <td>{{ item.title }}</td><td>{{ item.overall }}</td><td>{{ item.structure }}</td> <td>{{ item.entity }}</td><td>{{ item.data }}</td><td>{{ item.faq }}</td><td>{{ item.trust }}</td> </tr> {% endfor %} </table> </body> </html> """ @app.route("/") def index(): with open("report.json", encoding="utf-8") as f: data = json.load(f) return render_template_string(INDEX_HTML, rows=data) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True)这里要注意,Flask内置服务器只适合本地或者内网用,千万不要直接暴露到公网。我最初图省事把它部署在一台有公网IP的云服务器上,第二天日志里全是扫描和爆破请求。后来我加了nginx反向代理和简单的token鉴权才解决问题。
3.5 完整数据流程串联
为了让你更好理解整个源码是怎么串起来的,我给出一个控制台脚本的示例。这也是我自己日常使用的入口。
# run.py import json, sys from content_loader import ContentLoader from keyword_analyzer import KeywordAnalyzer from scorer import GEOAnalyzer def main(urls_file: str): loader = ContentLoader() analyzer = KeywordAnalyzer(custom_dict="dict.txt") results = [] with open(urls_file, encoding="utf-8") as f: urls = [line.strip() for line in f if line.strip()] for url in urls: try: text = loader.fetch_text(url) entities = analyzer.extract_entities(text) geo = GEOAnalyzer(title=url, text=text, entities=entities, author="") result = geo.overall_score() results.append({"url": url, **result}) print(f"[OK] {url} -> {result['overall']}") except Exception as e: print(f"[ERR] {url} -> {e}", file=sys.stderr) with open("report.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) if __name__ == "__main__": main("urls.txt")4. 实测运行记录与关键参数调优
4.1 本地环境准备
我用一台普通的MacBook Pro M1作为运行环境,Python版本3.11。整个环境的搭建过程大概三分钟:创建虚拟环境、安装依赖、下载stopwords.txt和自定义词典。
python3 -m venv venv source venv/bin/activate pip install -r requirements.txt运行前需要准备好三个文件:urls.txt(待分析的文章链接列表)、dict.txt(业务词自定义词典)、stopwords.txt(停用词表)。urls.txt每行一个URL,注意不要放过多链接,建议一次不超过500个,否则采集时间会很长。
4.2 核心参数设定逻辑
这个项目里有两个参数最影响分析效果:一是jieba的topK,二是评分器里的avg_len阈值。topK我默认设置为20,意思是每篇文章提取20个关键词。如果文章很长,建议调到30到40;如果文章很短,15就够。avg_len阈值是用来判断段落平均长度的,默认120字符每行。这个值不是拍脑袋定的,我统计过几篇被AI频繁引用的技术文章,正文平均行长度基本落在60到100之间,超过120就有"长篇大论"的嫌疑。
我还自定义了三个权重:structure 0.25、entity 0.20、data 0.20、faq 0.15、trust 0.20。这套权重是基于内容类型为"技术博客/产品介绍"的经验值。如果你的内容偏资讯快讯,可以把data权重调高,faq权重调低;如果做知识科普,trust权重应该加到0.3。没有一套万能的权重,但你可以通过跑一遍历史文章来反推适合自己的参数。
4.3 运行效果与数据观察
我拿自己的博客挑了一批文章跑测试,输出结果符合我的预期。结构清晰、带数据图表的文章,总分普遍在75分以上;而一些早年发布的"日记型"文章,总分只有50出头。差异主要体现在data和structure两个维度。
最有参考价值的是报表里暴露出来的共性短板。比如我有一批文章内容不错,但完全没有FAQ格式,导致faq得分几乎为0,把总分拉低了。后来我针对这批文章批量追加了"常见问题"区块,faq得分涨上去的同时,确实看到AI搜索的引荐流量有一定提升。
5. 常见问题与排查技巧实录
5.1 采集阶段:请求超时与内容为空
如果你在跑ContentLoader时遇到超时,先检查网络环境,其次看目标站点是否做了反爬。headers里的User-Agent建议改成一个常规浏览器的UA,不要用默认的Python-requests,很多站点会直接拒绝。另外,如果返回的text为空,多半是页面内容走的是JavaScript渲染,requests拿不到HTML正文。这种情况我建议用Selenium或者Playwright做渲染采集,但会显著增加部署复杂度,所以我在源码里刻意没集成。
5.2 中文分词阶段:关键词提取结果混乱
这是被问到最多的问题。现象是提取出来的关键词包含大量单字、无意义词汇,比如"了""的""是"。原因通常有两个:没有加载停用词表,或者自定义词典没有生效。停用词表找一个通用的中文停用词库即可,自定义词典每行一个词,格式就是纯文本。另外要注意jieba.load_userdict必须在分词前调用,放在import之后立刻执行最稳妥。
5.3 评分阶段:优化建议不够准确
有朋友反馈说评分偏低但在Google排名很好,这是正常的。GEO和SEO本来就是两套评价体系。如果你的内容在传统搜索引擎里表现好,但GEO评分低,说明你的内容偏向"搜索友好"而非"AI友好"。这时候不用怀疑源码的逻辑,要追问的是:你到底想在新渠道获得流量,还是守住旧阵地?我的建议是两边都抓,传统SEO别丢,但新内容一定要按GEO标准来写。
5.4 部署阶段:Flask端口占用与中文乱码
端口占用是老问题,改app.run里的port参数就行。中文乱码则要检查两个点:JSON文件保存时是否指定了ensure_ascii=False,以及Flask模板里是否有<meta charset="utf-8">。这两处少一个都会在报表页面看到一堆\uXXXX转义字符。
6. 落地后的一点个人体会
源码能帮助你发现问题,但真正决定GEO优化效果的,还是内容本身。我这几个月最大的体会是,AI引擎越来越像一个"挑剔的编辑",它不关心你关键词堆了多少,只关心内容是否组织得让读者(或者说模型)能快速抓住重点。结构清晰、实体明确、数据可验证的内容,在GEO维度天然是高分。
如果你准备上手这套源码,我的建议是先拿自己最优质的10篇文章跑一遍,把评分结果和你在AI搜索产品里的实测引用情况做一次对比。这个对比能帮你校准权重,也能帮你建立对GEO优化的感觉。源码本身不是终点,它只是帮你把"AI如何看你的内容"这件事从黑盒变成白盒的工具。
本文还有配套的精品资源,点击获取