编程 零依赖构建搜索引擎:只用 Python 3.14 标准库

2026-09-05 20:39:28

零依赖构建搜索引擎:只用 Python 3.14 标准库

几乎每个现代 Web 应用在某个时刻都会需要搜索功能。而通常的做法都是一样的:pip install elasticsearch whoosh fastapi uvicorn nltk click,几分钟后就有了一个功能完整但依赖庞大的搜索方案。

一位开发者决定挑战这个常规做法,只用 Python 3.14 的标准库构建了一个零依赖的搜索引擎。

为什么要零依赖

依赖管理是现代软件开发中最令人头疼的问题之一。每添加一个依赖,就意味着:

  • 更多的安全攻击面
  • 更复杂的版本兼容性问题
  • 更大的部署体积
  • 更慢的安装速度
  • 更多的维护负担

对于很多中小型应用来说,Elasticsearch 这样的重型解决方案可能是过度设计。一个只需要搜索几千篇文章的博客,真的需要一个分布式搜索引擎吗?

零依赖方案的优势在于:简单、轻量、可预测、易于部署和维护。

Python 标准库能做什么

Python 标准库虽然常被认为"电池已包含",但很多开发者可能没有意识到它包含了多少功能。对于构建搜索引擎来说,标准库提供了以下关键组件:

1. 数据结构

  • dict:哈希表,用于倒排索引
  • set:集合,用于布尔查询
  • collections.defaultdict:自动初始化的字典
  • collections.Counter:计数器,用于词频统计

2. 文本处理

  • re:正则表达式,用于分词和文本清洗
  • string:字符串常量和工具函数
  • unicodedata:Unicode 数据处理,用于规范化
  • html:HTML 实体解码

3. 数学和统计

  • math:数学函数,包括对数(用于 TF-IDF 计算)
  • statistics:统计函数

4. 文件和序列化

  • json:JSON 序列化,用于持久化索引
  • sqlite3:SQLite 数据库,可选的存储后端
  • pickle:Python 对象序列化
  • gzip:压缩,用于减小索引体积

5. 网络和 Web

  • http.server:HTTP 服务器,用于提供搜索 API
  • urllib:URL 处理
  • html.parser:HTML 解析,用于抓取网页内容

核心组件实现

分词器

分词是搜索引擎的第一步。一个简单的分词器可以用正则表达式实现:

import re
import string

def tokenize(text):
    # 转小写
    text = text.lower()
    # 用非字母数字字符分割
    tokens = re.findall(r'[a-z0-9]+', text)
    # 移除停用词
    stop_words = {'the', 'a', 'an', 'is', 'are', 'was', 'were', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or', 'but'}
    return [t for t in tokens if t not in stop_words and len(t) > 1]

倒排索引

倒排索引是搜索引擎的核心数据结构。它将每个词映射到包含该词的文档列表:

from collections import defaultdict

class InvertedIndex:
    def __init__(self):
        self.index = defaultdict(list)  # term -> [(doc_id, position, ...)]
        self.doc_lengths = {}  # doc_id -> length
        self.doc_count = 0
    
    def add_document(self, doc_id, text):
        tokens = tokenize(text)
        self.doc_lengths[doc_id] = len(tokens)
        self.doc_count += 1
        for pos, term in enumerate(tokens):
            self.index[term].append((doc_id, pos))

TF-IDF 评分

TF-IDF(词频-逆文档频率)是最经典的搜索排序算法:

import math

def tf_idf_score(self, term, doc_id):
    # 词频:该词在该文档中出现的次数
    tf = sum(1 for d, _ in self.index[term] if d == doc_id)
    # 逆文档频率:包含该词的文档数的倒数
    df = len(set(d for d, _ in self.index[term]))
    idf = math.log(self.doc_count / (df + 1))
    return tf * idf

搜索接口

将以上组件组合起来,就得到了一个基本的搜索接口:

def search(self, query, top_k=10):
    query_tokens = tokenize(query)
    scores = defaultdict(float)
    
    for term in query_tokens:
        if term not in self.index:
            continue
        for doc_id, _ in self.index[term]:
            scores[doc_id] += self.tf_idf_score(term, doc_id)
    
    # 按分数排序,返回 top_k
    results = sorted(scores.items(), key=lambda x: -x[1])
    return results[:top_k]

Web API

http.server 提供一个简单的 HTTP 搜索接口:

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class SearchHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith('/search'):
            query = self.path.split('q=')[1] if 'q=' in self.path else ''
            results = index.search(query)
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(json.dumps(results).encode())

性能和局限

零依赖方案在性能上当然无法与 Elasticsearch 等专业搜索引擎相比,但对于很多场景已经足够:

  • 索引速度:每秒可以处理数千篇文档
  • 搜索速度:在百万级文档上,单次搜索通常在几十毫秒内
  • 内存占用:索引可以按需加载,内存占用可控

主要局限包括:

  • 不支持分布式
  • 不支持实时索引(需要全量重建)
  • 分词功能简单(不支持中文分词、词干提取等高级功能)
  • 排序算法基础(只有 TF-IDF,没有 BM25、学习排序等)

什么时候该用零依赖方案

零依赖搜索引擎适合以下场景:

  • 个人博客、文档站点等小规模搜索
  • 离线工具、CLI 工具中的搜索功能
  • 原型开发和概念验证
  • 对部署体积和启动速度有严格要求的环境
  • 学习搜索引擎原理的教学项目

不适合的场景:

  • 大规模生产环境(百万级以上文档)
  • 需要复杂查询语法和高级排序
  • 需要分布式和高可用
  • 需要中文等复杂语言的分词

总结

只用 Python 标准库构建搜索引擎是一个有趣且有教育意义的项目。它证明了很多时候我们并不需要庞大的依赖栈,标准库已经提供了足够的基础组件。

当然,这不是说 Elasticsearch 和 Whoosh 这样的工具没有价值——它们在大规模和复杂场景下仍然是不可替代的。但对于很多中小型应用来说,一个零依赖的简单方案可能更合适:更简单、更轻量、更易于理解和维护。

这个项目也提醒我们:在添加依赖之前,先想想标准库是否已经足够。有时候,少即是多。

原文链接:https://dev.to/rushikesh_shedage/i-built-a-search-engine-with-zero-dependencies-just-python-314s-standard-library-2ob2

推荐文章

程序员茄子在线接单