文章目录

  • BoostSearch / 搜索引擎
    • 开发语言
    • 开发环境
    • 依赖库
    • 项目简介
    • 项目特点
    • 项目链接
    • 演示
  • 项目介绍与难点分析
    • 解析
    • 分词
    • 索引
      • 正排索引
      • 倒排索引
    • 权重
    • 包装

BoostSearch / 搜索引擎

开发语言

C/C++,HTML,CSS,JavaScript

开发环境

CentOS7、vim、g++、gdb、git、Makefile

依赖库

jsoncpp, cppjieba, http-lib, boost

项目简介

BoostSearch是一个基于boost文档的站内搜索引擎,当用户在页面上输入查询词后,就会快速的查询出相关的boost在线文档,弥补了boost在线文档中没有搜索的缺陷。

项目特点

  • 对离线版本的html文档进行解析,将解析结果整理为一个行本文文件。
  • 读取处理好的行文本文件进行分词、权重计算等操作,在内存中构造出正排索引和倒排索引。
  • 对查询词进行分词、触发,依据相关度对查询结果进行排序,并以Json格式进行包装后序列化为字符串返回。
  • 通过HTTP服务器搭载搜索页面, 为外部提供服务。

项目链接

github

演示


项目介绍与难点分析

具体的模块划分以及功能分析我以上图的形式展现了出来,本文主要说明项目中的一些难点以及问题分析,至于具体实现以及其他内容请参考源代码
https://github.com/HONGYU-LEE/BoostSearch

解析

无论是站内搜索引擎还是全网搜索引擎,第一步都必须对获取的文件进行一个预处理

预处理分为两个步骤

  1. 找出所有的HTML文档
  2. 提取出文档中的关键信息

无论是官方提供的文档文件,还是通过爬虫在网上爬取的文件,都会夹杂着许多除了HTML外的例如图片、音乐、测试代码等搜索引擎不需要的东西,所以第一步就是将从这些杂乱的文件中,找出所有的HTML。

而C++标准库中并没有提供一个能够遍历文件的方法,而linux的系统函数dirent也可以做到,但是在我上一个项目MiniFTP中获取列表那一块可以看到,这玩意并不好用,所以这里我用的是Boost库中的filesystem模块。它会通过一个迭代器来递归找到该目录下的所有文件,来达到遍历的效果

找到文件后,第二步就是将文章中的关键信息提取出来

我们需要的信息只有三个,标题、URL、正文

标题:标题的提取方法很简单,只需要找到HTML中title这个标签中的内容即可

URL:URL的处理也很简单,通过查询官方在线文档,可以看到URL的构成就是官方文档地址加上文件名,所以我们只需要提取出之前枚举的文件的名字,将其和文档地址进行组合,就可以获得到URL

正文:接下来我们需要做的,就是过滤掉所有的标签以及标签中的内容,剩下的text内容就是我们需要的正文。并且为了之后方便处理,将所有的换行符全部替换成为空格,将每篇的正文都单独处理成一行。

提取出信息后,就需要对信息进行划分并保存。

我们所要做的,就是将每一篇文档的信息全部汇总到一行中,即处理为一个行文本文件,使得每行数据就对对应着一篇文档,这样我们在后续处理的时候只需要按行来提取出信息,就可以达到遍历所有数据的效果。

接着,还需要通过某种方式来划分开标题、URL、正文

考虑到这是一个行文本文件,换行、空格、回车等符号则直接pass,并且作为分隔符的符号必须要在文章中没有出现过,考虑到这一点,就可以使用不可见字符(文章中不可能出现的字符)

在这里我选择的是\1(SOH符号),通过这个符号对所有的标题、URL、正文进行划分。

搜索的本质其实就是找到出现过用户查询内容的文章

要做到这一点其实并不复杂,可以通过比对文章内容以及用户查询词来完成。

所以这时需要进行两个步骤,一个是分词,一个是索引

分词

分词很好理解,就是将用户输入的字符串,分解成为一个一个的关键词,然后再通过关键词去进行内容的查找。

例如对“今天的天气很好啊”这句话进行分词

今天/的/天气/很好/啊

对于我们人来说,由于我们有自主思考的能力,加上了出生至今的经验以及积累,分词对于我们来说是很简单的一件事,但是对于计算机而言,这并不是一件简单的事情。

例如这句出自神雕侠侣中的一句话,需要结合其中的背景来理解,机器则很难理解

“我也想过过儿过过的生活” = 我/也想/过/过儿/过/过的/生活

并且有些句子可能会解析出多种结果

“在北京大学生活区喝进口红酒”

1.在/北京/大学/生活区/喝/进口/红酒

2.在/北京大学/生活区/喝/进口红酒

3.在/北京/大学生活区/喝/进口红酒

并且在句子中会经常出现一些如“的”,“了”,“呢”,“吗”,"像"等等等语气词或者结束词会不可避免的大量出现,但是这些词又没有什么实际上的意义,所以我们将其称为**“暂停词/停用词”**,不会将其放入权重的计算中。

考虑到分词的困难巨大,甚至比我们整个项目都要复杂,所以这里就不再自己造轮子,而是引入了第三方库结巴分词——https://github.com/yanyiwu/cppjieba

索引

接着就到了整个项目中最核心的部分,索引。

即使本项目是一个站内的搜索引擎,只有8000多篇文档, 但是实际算下来字符数量可能多达千万甚至更多,如果直接进行遍历搜索,那么速度将会慢的吓人。而在庞大的数据中想要快速的查询到指定的数据,那么马上就能想到的方法就是建立索引

索引就相当与我们看书时文章的一个目录,我们可以通过目录上的页数来快速定位到需要查询的数据,所以我们以下两种索引,正排索引倒排索引

正排索引

正排索引的作用即根据文档ID来查找文档信息

//文章信息
struct DocInfo
{int64_t doc_id;    //文档IDstring title; //文档标题string url;       //文档URLstring content; //文档正文
};

因为我们最终需要查询的结果只需要文档的标题、URL、正文,所以正排索引所存储的文档信息就使用上面这种结构。

vector<DocInfo> forward_index;  //正排索引

接着,就需要建立起ID与文档信息的映射关系,由于文档本身并没有规定固定的ID,所以这里我就直接使用数组下标来作为文档的ID,将其以vector的形式进行存储(如果规定了ID就使用unordered_map)

倒排索引

倒排索引的作用即根据文档片段来查找相关的文档ID

//权重信息
struct Weight
{int64_t doc_id;    //文档IDint weight;   //权重string word;    //关键词
};
typedef vector<Weight> InvertedList;  //倒排拉链

我使用上面的Weight结构来存储每个关键词所对应出现的文章与该文章的权重大小。

由于一个Weight对应着该词的其中一篇相关文章,所以为了方便查询,我将该词的所有相关文章以一个vector进行存储,构成一个倒排拉链的结构,并将这个倒排拉链作为倒排索引的节点。这样通过关键词来查找到倒排拉链时,就可以通过直接遍历倒排拉链来获取到所有的相关文档。

unordered_map<string, InvertedList> invert_index;   //倒排索引

关键词与相关文档的映射使用哈希表(unordered_map)建立

下面介绍下索引的建立流程

假设我们此时有两篇文章,首先进行分词

接着,根据文档信息构建出正排索引

文档ID——》文档信息

接着,依据每个关键词出现过的文章,构建出倒排索引

关键词——》相关文档ID

建立了这样一个索引体系之后,在查询时,我们只需要对查询词进行分词,再通过倒排索引查询到对应的倒排拉链,权重信息,来为每个倒排拉链的文档进行排序,就可以根据相关度高低以此获取到对应的文档ID,再通过正排索引就可以获取到我们需要的文档信息了。

权重

由于我们存储的数据量不是很大,只有大概8000多个文档,并且该项目只是用于学习搜索引擎原理,所以这里就使用线性公式来进行权重的计算,计算的依据就是关键词的词频

首先用以下结构来统计关键词在文章的正文以及标题中分别出现的次数

struct WordCount
{WordCount() : title_count(0), content_count(0){}    int title_count; //标题出现频率int content_count;   //正文出现频率
};

关键词在正文中和标题中出现的权重应该不一样。

因为标题即对正文的高度概括,在标题中出现的内容往往与实际内容相关,而在正文即使出现,也有可能只是顺口一提或者引用,所以我认为在标题中出现的权重应该要比在正文出现要高

所以推导的公式如下:权重 = 标题出现次数 * 10 + 正文出现次数

//权重 = 标题出现次数 * 10 + 正文出现次数
weight.weight = word_pair.second.title_count * 10 + word_pair.second.content_count;

包装

对于查询的结果,由于我们并不需要将所有的信息都展示在搜索页面上,只需要展示关键信息,所以我们需要的内容只有标题、URL、描述。

前两个已经获取了,但是描述还需要我们自己从正文中提取。

关于描述信息的提取,我的思路如下。

  1. 查询关键词在文章中第一次出现的位置,获取接近这个位置的前后内容当作描述信息
  2. 因为大部分描述信息的规律都是先提出关键词,然后对关键词进行解释,所以我们尽量保证关键词的位置在描述片段中靠前
  3. 描述信息要简短,如果内容过长则用省略号代替
  4. 如果关键词未找到,则说明关键词在标题中,直接从头截取一段内容当作描述

根据以上条件,我推导出以下描述信息提取公式

返回内容 = 150字节 = 关键词往前50字节 + 关键词(包括关键词)往后100个字节

  1. 正常情况下,选择关键词位置的前50个字节的位置作为起始位置,返回起始位置往后的150个字节。
  2. 当前面不足50个时,就从起点位置往后直接返回150个字节。当末尾不足时,直接返回到结束位置。如果后面还有数据用省略号表示。
  3. 如果正文中未找到关键词,则说明关键词在标题中,直接从起始位置开始返回150字节

这样,我们就可以构建出最后查询出的结果。

但是我们最终查询的结果并不能直接返回给调用者,还需要对其进行包装

这里我采用的是JSON格式,借助了第三方库JSONCPP(https://github.com/open-source-parsers/jsoncpp)来进行包装

JSON的格式如下

[{"title" : "标题","url" : "url","desc"  : "描述"}{"title" : "标题","url"    : "url","desc"  : "描述"}{"title" : "标题","url"    : "url","desc"  : "描述"}..................
]

之后将结果序列化为字符串回复给调用者

【项目介绍】搜索引擎相关推荐

  1. 欢迎来到美多商城!-项目准备之项目介绍-项目需求分析-项目架构设计

    欢迎来到美多商城! [前后端不分离的项目] 项目介绍 项目需求分析 需求分析原因: 可以整体的了解项目的业务流程和主要的业务需求. 项目中,需求驱动开发.即开发人员需要以需求为目标来实现业务逻辑. 需 ...

  2. 手把手教你做关键词匹配项目(搜索引擎)---- 第九天

    第九天 回顾: 8. 手把手教你做关键词匹配项目(搜索引擎)---- 第八天 7. 手把手教你做关键词匹配项目(搜索引擎)---- 第七天 6. 手把手教你做关键词匹配项目(搜索引擎)---- 第六天 ...

  3. 港科招生 | 香港科大DBA(工商管理博士)项目介绍和科研成果分享

    香港科大DBA(工商管理博士) 项目介绍和科研成果分享 科技会减少我们对官方数据的依赖吗? 在追求卓越研究和创新思维的抱负下,香港科技大学商学院延续其 30 年的卓越历史,坚定不移地致力于学术独创性和 ...

  4. 广告点击率预测-项目介绍

    项目介绍 项目介绍 KDD CUP 2012 Track2 1.给定查询和用户信息后预测广告点击率 搜索广告是近年来互联网的主流营收来源之一.在搜索广告背后,一个关键技术就是点击率预测-–pCTR(p ...

  5. 【项目介绍】ElasticSearch7+Spark 构建高相关性搜索服务千人千面推荐系统

    我做的项目是在慕课网买的 项目介绍 项目需求背景:模仿大众点评应用提供用户线下搜索推荐服务门店的需求 技术选型:后端业务:SpringBoot:后端存储:MySQL.mybatis接入:搜索系统:El ...

  6. 电影推荐系统项目 介绍(一)

    美图欣赏: 一.新鲜一感 半山腰总是挤的,记得上山顶看看,那里有最好的星空 二.项目介绍 1..推荐系统是什么? 信息过载时代所采用的措施.面对海量的数据信息,从中快速推荐出符合用户特点的物品. 受众 ...

  7. 完整电商项目--项目介绍(1)

    文章目录 项目开发模式 项目介绍 归纳项目的主要模块 技术点 总结: 本项目不仅是对 电商项目的开发学习,也是对上面所提到的技术的 深入理解和教学!可能会跳出项目本身去讲解 相关技术点的内容,总体来说 ...

  8. sourceforge优秀项目介绍

    FROM JAVAEYE sourceforge优秀项目介绍之一:PowerFolder 文件同步 项目名称 :PowerFloolder 文件同步  成立日期:2003年底  项目介绍:  主旨:P ...

  9. windows下nodejs express安装及入门网站,视频资料,开源项目介绍

    windows下nodejs express安装及入门网站,视频资料,开源项目介绍,pm2,supervisor,npm,Pomelo,Grunt安装使用注意事项等总结 第一步:下载安装文件 下载地址 ...

最新文章

  1. 成功解决在excel表格中仅在某列内插入一个空白单元格,使其下部的数据整体下移一格
  2. 【知识图谱】知识抽取与挖掘(I)
  3. signature=18441de5a4bb8df92eb5cf5dcca47d1e,Heroísmo y los medios de comunicación (Capítulo 5A 5B)...
  4. 首字母大写转换 java,Java InitialsTransformation(字符串首字母大小写转换)
  5. JSON_dump和load
  6. MapInfo地图投影的添加
  7. Android 源码下载并编译Rom
  8. 元器件及测量基础实验
  9. CAE软件技术现状调研
  10. 阿兹尔海默症生物标志物(姑且叫标志物)的一些总结
  11. SICP 第一章的练习
  12. 牛客网力扣算法编程之二十一 | 数组 - 明明的随机数 - Java代码实现
  13. 1.1微信支付之现金红包 - Java 开发
  14. cad面积计算机,用cad计算多个面积的方法步骤
  15. 【Python问题解决】---- ERROR: Could not install packages due to an OSError: [WinError 2] 系统找不到指定的文件。
  16. 金融伦理学(Financial Ethcs)笔记
  17. JDBC jar下载
  18. 五彩斑斓的 Black —— Python代码格式化工具
  19. jQuery 实现音乐导航案例
  20. php fpm ts nts,如何判断PHP 是ts还是nts版的?

热门文章

  1. SpringBoot_入门-springboot-helloworld
  2. ad09只在一定范围内查找相似对象_kafka日志段中的二分查找
  3. AbstractQueuedSynchronizer源码解析
  4. html动画效果放大,一个CSS+jQuery实现的放大缩小动画效果
  5. Android端发送字符到Wed端,Android面试-socket和websocket
  6. 简明Git与GitHub命令使用教程
  7. Swift基础--方法
  8. 详解JavaScript之神奇的Object.defineProperty
  9. Llama-impala on yarn的中间协调服务
  10. JS高效关键字搜索---转