搜索引擎极大地方便了互联网生活,也成为上网必不可少的刚需工具。依托搜索引擎发展起来的互联网广告,则成了硅谷和中国巨头的核心商业模式;而搜索本身,也在持续进步着, Facebook 和微信也一直有意向在自家社交产品架设搜索平台。
关于搜索引擎的价值就不必多说了,本节主要来看一下搜索引擎的核心构成。
要知道,一个搜索引擎由搜索器、索引器、检索器和用户接口四个部分组成。
搜索器,通俗来讲就是我们常提到的爬虫(scrawler),它能在互联网上大量爬取各类网站的内容,送给索引器。索引器拿到网页和内容后,会对内容进行处理,形成索引(index),存储于内部的数据库等待检索。
**后的用户接口很好理解,是指网页和 App 前端界面,例如百度和谷歌的搜索页面。用户通过用户接口,向搜索引擎发出询问(query),询问解析后送达检索器;检索器高效检索后,再将结果返回给用户。
为了方便,这里只提供五个文件的检索,各文件存放的内容分别如下:爬虫知识不是本节学习的重点,不做深入介绍,我们假设搜索样本存在于本地磁盘上。
# 1.txt
考高分网
# 2.txt
http://www.pxcodes.com
# 3.txt
「考高分网」是一个在线学习编程的网站,我们发布了多套文字教程,它们都通俗易懂,深入浅出。
# 4.txt
考高分网成立于 2012 年初,目前已经运营了将近 7 年,我们致力于分享精品教程,帮助对编程感兴趣的读者。
# 5.txt
坚持做好一件事情,做到极致,让自己感动,让用户心动,这就是足以传世的作品!
先来定义 SearchEngineBase 基类,代码如下所示:
class SearchEngineBase(object):
def __init__(self):
pass
def add_corpus(self, file_path):
with open(file_path, 'rb') as fin:
text = fin.read().decode('utf-8')
self.process_corpus(file_path, text)
def process_corpus(self, id, text):
raise Exception('process_corpus not implemented.')
def search(self, query):
raise Exception('search not implemented.')
def main(search_engine):
for file_path in ['1.txt', '2.txt', '3.txt', '4.txt', '5.txt']:
search_engine.add_corpus(file_path)
while True:
query = input()
results = search_engine.search(query)
print('found {} result(s):'.format(len(results)))
for result in results:
print(result) SearchEngineBase 类可以被继承,继承的类分别代表不同的算法引擎。每一个引擎都应该实现 process_corpus() 和 search() 两个函数,对应刚刚提到的索引器和检索器。main() 函数提供搜索器和用户接口,于是一个简单的包装界面就有了。具体来看这段代码,其中 add_corpus() 函数负责读取文件内容,将文件路径作为 ID,连同内容一起送到 process_corpus 中;process_corpus 需要对内容进行处理,然后文件路径为 ID ,将处理后的内容存下来。处理后的内容,就叫做索引(index);search 则给定一个询问,处理询问,再通过索引检索,然后返回。
理解了上面这些概念后,接下来实现一个**基本的可以工作的搜索引擎,代码如下:
class SimpleEngine(SearchEngineBase):
def __init__(self):
super(SimpleEngine, self).__init__()
self.__id_to_texts = {}
def process_corpus(self, id, text):
self.__id_to_texts[id] = text
def search(self, query):
results = []
for id, text in self.__id_to_texts.items():
if query in text:
results.append(id)
return results
search_engine = SimpleEngine()
main(search_engine) 运行结果为:
C语言中文网
found 3 result(s):
1.txt
3.txt
4.txt
- SimpleEngine 实现了一个继承 SearchEngineBase 的子类,继承并实现了 process_corpus 和 search 接口,同时,也顺手继承了 add_corpus 函数(当然重写也是可行的),因此可以在 main() 函数中直接调取。
- 在新的构造函数中,self.__id_to_texts = {} 初始化了自己的私有变量,也就是这个用来存储文件名到文件内容的字典。
- process_corpus() 函数则非常直白地将文件内容插入到字典中。这里注意,ID 需要是**的,不然相同 ID 的新内容会覆盖掉旧的内容。
- search 直接枚举字典,从中找到要搜索的字符串。如果能够找到,则将 ID 放到结果列表中,**后返回。
是不是非常简单呢?整个过程始终贯穿着面向对象的思想。



