搜索引擎的简单版本,本质上是一个小型的信息检索系统。它的核心不是模仿大型商业搜索引擎的界面,而是实现“网页采集→文本解析→索引构建→查询排序”的完整闭环。下面按数据流程,说明如何从零制作一个专业、可运行的简单版搜索引擎。

爬虫是搜索引擎的数据入口。它的任务是给定一组种子 URL,循环将这些 URL 放入待抓取队列,下载网页后解析出新的链接,再规范化链接并判断是否已经抓取。简单版可以限定站点范围,比如只抓取当前域名下的页面,避免爬到外网导致抓取失控。
抓取得到的原始 HTML 需要清洗成纯文本。要提取
清洗后的网页称为一篇文档。为每篇文档分配唯一编号 docID,并把文档的 URL、标题、正文长度存储到文档表。接下来建立倒排索引,即“词语 → 文档列表”的映射。每个词语对应一个 Posting List(倒排列表),其中每条记录至少包含 docID 和词频 TF。构建倒排索引时,每个网页中出现的每个词语,都需要写入对应列表。
如果希望支持“短语搜索”或“精确匹配”,倒排列表还要记录词语在文档中的位置信息。简单版可以先用“单词匹配+排序”实现基本功能,不必加入复杂的位置检索。
存储可以用 SQLite。简单版建议建三张表:doc 表保存 docID、URL、标题、文档长度;term 表保存词语及其文档频率 df;posting 表保存 termID、docID、词频 TF 和位置信息。使用 B-tree 索引加快词语查询。使用 SQLite 而不是全部放在内存,原因是数据量增大后内存排序困难,而 SQLite 提供持久化和简单的 SQL 查询能力。
用户在搜索框输入词后,查询服务首先对查询词执行与建索引时相同的分词和停用词过滤,得到查询词序列。然后在倒排索引中查找每个词对应的 Posting List。简单版可用布尔检索:所有词之间用 AND 表示必须全部出现,用 OR 表示出现任意一个即可;一般搜索引擎默认是“多词同时召回并排序”,即近似 AND。
召回后要对文档打分排序。最简单的专业算法是 TF-IDF。TF 是词在文档中出现次数除以文档总词数,IDF 是 log(N/(df+1)),其中 N 是文档总数,df 是包含该词的文档数。将查询词和文档分别表示成 TF-IDF 向量,计算它们的余弦相似度,得分越高排名越靠前。
更推荐的标准排序算法是 BM25。BM25 是 TF-IDF 的改进版,它考虑了文档长度和词频的非线性增长。公式中常用参数值为 k1=1.2 到 2.0,b=0.75。实现 BM25 只需要倒排索引中的 docID、TF、文档总长和文档总数,因此非常适合作为简单版搜索引擎的排序模型。
最后,系统按分数从高到低取出 Top K 篇文档,从文档表中取出标题、URL,并截取包含查询词的一段正文作为摘要。如果想让体验更好,可以在摘要中对查询词加高亮;高亮在简单版中通常只需在 HTML 中把匹配词包上标签,但注意避免把高亮样式写进内部索引。
前端只需一个搜索框和一个结果列表。可以用 Python Flask 创建 /search 路由,接收 query 参数,调用后端的搜索函数,返回渲染后的结果页。不要在这个阶段引入 Elasticsearch,虽然它很成熟,但并不能体现搜索引擎的完整原理。
简单版需要注意的几个工程问题:抓取时要控制请求频率,不能高并发打爆目标站点;尽量遵守 robots.txt 和网站的服务条款;对 URL 做规范化与去重,防止相同内容产生多个入口;对大型种子站,应使用布隆过滤器存储已抓取 URL,节约内存。
上述实现适合教学、站内搜索或小规模垂直搜索。真正的全网搜索引擎还需要额外解决:分布式网页抓取、增量更新、页面重要度计算、链接分析(PageRank)、Query 改写与纠错、个性化排序、索引分片和缓存加速等。简单版把这些模块全部省略,因此在覆盖率和质量上无法与商业产品相比。
制作简单版搜索引擎,最重要的不是写多少代码,而是理解倒排索引和相关性排序这两个核心理念。使用 Python、SQLite、jieba 与 Flask,足够在几天内完成一个具备“爬虫+索引+搜索”的最小系统。之后扩展时,再将爬虫和索引拆分到不同进程,把 SQLite 换成分布式索引,就向真正的搜索引擎靠近了。

查看详情

查看详情