首页 > 编程知识 正文

搜索引擎架构,bt搜索引擎

时间:2023-05-05 18:02:00 阅读:9988 作者:3469

系统介绍搜索引擎大致分为四个部分。 是收集、分析、索引和查询。

收集是指使用我们常说的爬行动物爬页面。

分析主要负责网页内容提取、分词、临时索引构建、PageRank值计算等部分。

索引主要负责根据分析阶段获得的临时索引构建倒排索引。

查询主要根据用户的请求,基于倒排索引获取相关页面,计算页面排名,并将查询结果返回给用户。

搜索引擎将整个互联网视为数据结构的有向图,将每一页视为顶点。 如果一个页面包含指向另一个页面的链接,请在两个顶点之间连接有向边。 我们可以利用图的遍历搜索算法遍历整个互联网的网页。

前面介绍了两种图的扫描方法,深度优先和广度优先。 搜索引擎采用广度优先的搜索策略。 具体来说,首先要查找知名页面(专业称谓权重较高)的链接) (例如新浪主页或腾讯主页等),作为种子页面的链接排队。 根据爬虫优先策略,从队列中提取链接,进入相应页面,分析页面中其他页面的链接,并将分析的链接添加到队列中。

为什么要使用广度优先,是因为搜索引擎会优先爬上权重较高的页面,所以离种子页面越近权重越高,广度优先越有可能合适。

基本原理就是这么简单。 但是,要运行到实现水平,有很多技术细节。 使用与收集阶段有关的几个重要文件,说明收集工程有哪些重要的技术细节。

1 .要获取web链接文件,links.bin在搜索页面时按大小顺序排列,爬虫会不断解析页面链接并对其进行排队。 这样,队列中的链接可能会增加,内存可能无法释放。 因此,使用存储在磁盘上的文件(links.bin )作为广度优先搜索的队列。 爬行动物从links.bin文件中,提取链接并向上爬相应的页面。 登上网页后,将分析的链接直接保存到links.bin文件中。

这样,用文件保存指向网页的链接还有其他好处。 例如,它支持断点的连续攀登。 也就是说,机器断电后,指向网页的链接不会丢失。 机器重启后,也可以从以前攀登的位置继续攀登。

关于如何解析页面获取链接,我额外说几句。 将整个页面视为大字符串,可以利用字符串匹配算法在这个大字符串中搜索这样的网页标签,并按顺序读取它们之间的字符串。 这其实是网页的链接。

2 .网页判断文件: bloom_filter.bin如何避免重复获取同一网页? 这个问题在位图一节已经说过了。 使用光晕过滤器,可以快速、非常节约内存地实现网页的判定。

但是,仍然存在这些问题,如果将布隆过滤器保存在内存中,则在机器停机并重新启动后,布隆过滤器将变为空。 这可能会导致已获取的页面大量重复获取。

这个问题该怎么解决? 可以定期将光晕过滤器持久化到磁盘,例如,每30分钟保存到bloom_filter.bin文件中。 这样,即使发生机器停机,也只会丢失光晕过滤器的部分数据。 计算机重新启动后,可以重新读取磁盘中的bloom_filter.bin文件并将其恢复到内存中。

3 .原始网页保存文件: doc_raw.bin登上网页后,需要保存才能进行离线分析、索引。 如何保存大量原始的网络数据呢?

如果将每个网页另存为单独的文件,磁盘中的文件会非常多,可能会达到数千万甚至数亿。 很明显,典型的文件系统不适合存储这么大量的文件。 所以,我们可以把多个页面保存在一个文件里。 每页之间用一定的标识隔开,以后容易阅读。 的存储格式如下图所示。 其中,名为doc_id的字段是网页的编号。 稍后说明。

当然,这样的文件不能太大。 因为文件系统对文件的大小也有限制。 因此,每个文件的大小可以设置为不超过一定的值(例如1GB )。 随着更多网页添加到文件中,文件的大小越来越大。 如果超过1GB,将创建新文件以保存新爬网的网页。

假设一台计算机的硬盘大小为100GB左右,一个网页的平均大小为64KB。 它在一台机器上,我们可以存储100万到200万左右的页面。 假设我们的机器带宽为10MB,那么下载100GB的页面大约需要10000秒。 也就是说,要攀登100万以上的页面,只需要几个小时。

4 .网页链接及其编号对应文件: doc_id.bin刚才提到了网页编号这个概念,现在我来说明一下。 网页编号实际上是为每个网页分配唯一的ID,便于以后分析网页和索引网页。 怎么给网页编号?

我们可以按照页面被爬网的优先顺序,从小到大进行编号。 具体来说,我们维护中心的计数器,每爬一页,就从计数器中取一个号码,分配到这个页面,并在计数器上加1。 保存网页的同时,将指向网页的链接和编号之间的对应关系保存在单独的doc_id.bin文件中。

在爬虫浏览网页的过程中,有关的四个重要文件,我都介绍完了。 其中,有两个文件由爬行动物自己使用: links.bin和bloom_filter.bin。 其他两个(doc_raw.bin、doc_id.b

in)是作为搜集阶段的成果,供后面的分析、索引、查询用的。

分析

网页爬取下来之后,我们需要对网页进行离线分析。分

析阶段主要包括两个步骤,第一个是抽取网页文本信息,第二个是分词并创建临时索引。我们逐一来讲解。

1. 抽取网页文本信息。

网页是半结构化数据,里面夹杂着各种标签、JavaScript 代码、CSS 样式。对于搜索引擎来说,它只关心网页中的文本信息,也就是,网页显示在浏览器中时,能被用户肉眼看到的那部分信息。我们如何从半结构化的网页中,抽取出搜索引擎关系的文本信息呢?

我们之所以把网页叫作半结构化数据,是因为它本身是按照一定的规则来书写的。这个规则就是 HTML 语法规范。我们依靠 HTML 标签来抽取网页中的文本信息。这个抽取的过程,大体可以分为两步。

第一步是去掉 JavaScript 代码、CSS 格式以及下拉框中的内容(因为下拉框在用户不操作的情况下,也是看不到的)。也就是,,这三组标签之间的内容。我们可以利用 AC 自动机这种多模式串匹配算法,在网页这个大字符串中,一次性查找, ,

第二步是去掉所有 HTML 标签。这一步也是通过字符串匹配算法来实现的。过程跟第一步类似,我就不重复讲了。

2. 分词并创建临时索引

经过上面的处理之后,我们就从网页中抽取出了我们关心的文本信息。接下来,我们要对文本信息进行分词,并且创建临时索引。

对于英文网页来说,分词非常简单。我们只需要通过空格、标点符号等分隔符,将每个单词分割开来就可以了。但是,对于中文来说,分词就复杂太多了。我这里介绍一种比较简单的思路,基于字典和规则的分词方法。

其中,字典也叫词库,里面包含大量常用的词语(我们可以直接从网上下载别人整理好的)。我们借助词库并采用最长匹配规则,来对文本进行分词。所谓最长匹配,也就是匹配尽可能长的词语。我举个例子解释一下。

比如要分词的文本是“中国人民解放了”,我们词库中有“中国”“中国人”“中国人民”“中国人民解放军”这几个词,那我们就取最长匹配,也就是“中国人民”划为一个词,而不是把“中国”、“中国人”划为一个词。具体到实现层面,我们可以将词库中的单词,构建成 Trie 树结构,然后拿网页文本在 Trie 树中匹配。

每个网页的文本信息在分词完成之后,我们都得到一组单词列表。我们把单词与网页之间的对应关系,写入到一个临时索引文件中(tmp_Index.bin),这个临时索引文件用来构建倒排索引文件。临时索引文件的格式如下


在临时索引文件中,我们存储的是单词编号,也就是图中的 term_id,而非单词本身。这样做的目的主要是为了节省存储的空间。那这些单词的编号是怎么来的呢?

给单词编号的方式,跟给网页编号类似。我们维护一个计数器,每当从网页文本信息中分割出一个新的单词的时候,我们就从计数器中取一个编号,分配给它,然后计数器加一。

在这个过程中,我们还需要使用散列表,记录已经编过号的单词。在对网页文本信息分词的过程中,我们拿分割出来的单词,先到散列表中查找,如果找到,那就直接使用已有的编号;如果没有找到,我们再去计数器中拿号码,并且将这个新单词以及编号添加到散列表中。

当所有的网页处理(分词及写入临时索引)完成之后,我们再将这个单词跟编号之间的对应关系,写入到磁盘文件中,并命名为 term_id.bin。

经过分析阶段,我们得到了两个重要的文件。它们分别是临时索引文件(tmp_index.bin)和单词编号文件(term_id.bin)。

3. 摘要信息和网页快照

摘要信息:
增加 summary.bin 和 summary_offset.bin。在抽取网页文本信息后,取出前 80-160 个字作为摘要,写入到 summary.bin,并将偏移位置写入到 summary_offset.bin。
summary.bin 格式:
doc_id t summary_size t summary rnrn
summary_offset.bin 格式:
doc_id t offset rn
Google 搜索结果中显示的摘要是搜索词附近的文本。如果要实现这种效果,可以保存全部网页文本,构建搜索结果时,在网页文本中查找搜索词位置,截取搜索词附近文本。

网页快照:
可以把 doc_raw.bin 当作快照,增加 doc_raw_offset.bin 记录 doc_id 在 doc_raw.bin 中的偏移位置。
doc_raw_offset.bin 格式:
doc_id t offset rn

索引

索引阶段主要负责将分析阶段产生的临时索引,构建成倒排索引。倒排索引( Inverted index)中记录了每个单词以及包含它的网页列表。

我们刚刚讲到,在临时索引文件中,记录的是单词跟每个包含它的文档之间的对应关系。那如何通过临时索引文件,构建出倒排索引文件呢?

解决这个问题的方法有很多。考虑到临时索引文件很大,无法一次性加载到内存中,搜索引擎一般会选择使用多路归并排序的方法来实现。

我们先对临时索引文件,按照单词编号的大小进行排序。因为临时索引很大,所以一般基于内存的排序算法就没法处理这个问题了。我们可以用之前讲到的归并排序的处理思想,将其分割成多个小文件,先对每个小文件独立排序,最后再合并在一起。当然,实际的软件开发中,我们其实可以直接利用 MapReduce 来处理。

临时索引文件排序完成之后,相同的单词就被排列到了一起。我们只需要顺序地遍历排好序的临时索引文件,就能将每个单词对应的网页编号列表找出来,然后把它们存储在倒排索引文件中。具体的处理过程,我画成了一张图。通过图,你应该更容易理解。

除了倒排文件之外,我们还需要一个文件,来记录每个单词编号在倒排索引文件中的偏移位置。我们把这个文件命名为 term_offset.bin。这个文件的作用是,帮助我们快速地查找某个单词编号在倒排索引中存储的位置,进而快速地从倒排索引中读取单词编号对应的网页编号列表。


经过索引阶段的处理,我们得到了两个有价值的文件,它们分别是倒排索引文件(index.bin)和记录单词编号在索引文件中的偏移位置的文件(term_offset.bin)。

查询

doc_id.bin:记录网页链接和编号之间的对应关系。

term_id.bin:记录单词和编号之间的对应关系。

index.bin:倒排索引文件,记录每个单词编号以及对应包含它的网页编号列表。

term_offsert.bin:记录每个单词编号在倒排索引文件中的偏移位置。

这四个文件中,除了倒排索引文件(index.bin)比较大之外,其他的都比较小。为了方便快速查找数据,我们将其他三个文件都加载到内存中,并且组织成散列表这种数据结构。

当用户在搜索框中,输入某个查询文本的时候,我们先对用户输入的文本进行分词处理。假设分词之后,我们得到 k 个单词。

我们拿这 k 个单词,去 term_id.bin 对应的散列表中,查找对应的单词编号。经过这个查询之后,我们得到了这 k 个单词对应的单词编号。

我们拿这 k 个单词编号,去 term_offset.bin 对应的散列表中,查找每个单词编号在倒排索引文件中的偏移位置。经过这个查询之后,我们得到了 k 个偏移位置。

我们拿这 k 个偏移位置,去倒排索引(index.bin)中,查找 k 个单词对应的包含它的网页编号列表。经过这一步查询之后,我们得到了 k 个网页编号列表。

我们针对这 k 个网页编号列表,统计每个网页编号出现的次数。具体到实现层面,我们可以借助散列表来进行统计。统计得到的结果,我们按照出现次数的多少,从小到大排序。出现次数越多,说明包含越多的用户查询单词(用户输入的搜索文本,经过分词之后的单词)。

经过这一系列查询,我们就得到了一组排好序的网页编号。我们拿着网页编号,去 doc_id.bin 文件中查找对应的网页链接,分页显示给用户就可以了。

参考:
极客时间《数据结构与算法之美》

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。