目录
一.项目背景及原理
1.背景
2.原理
二.技术栈及项目环境
1.技术栈
2.项目环境
3.环境准备
三.模块划分
四. 遇到的问题及其解决方法
1.搜索结果出现重复文档的问题
2.实现httplib功能的问题
五. 项目特点
1.文档记录
2.竞价排名
3.去掉暂停词
4.模拟实现httplib库
六. 最终版代码
前言: 这里实现一个基于boost官方文档的搜索引擎。,但是不只可以用来搜索boost, 修改引入的内容, 就可以变成其它的搜索引擎, 比如可以用来搜索cpp官网文档内容等.
搜索引擎很多互联网大厂都做过,但是他们做的是由很多人共同完成的大型项目,是一个人没法完成的,不过,可以通过实现一个"小"的搜索引擎,来揣测出这些大型搜索引擎是如何做的。
这个搜索引擎项目就是在一个网站内搜索,搜索的数据更垂直,数据量更小,实现相对比较简单。
不过呢,虽然实现的是boost搜索引擎,但是只要修改所引入的文档内容,以及相应的就可以变成其它的搜索引擎,例如JAVA搜索引擎、STL搜索引擎等。
实现的内容:
我们用百度搜索引擎搜索Boost,可以看到网页的title、网页内容的摘要描述、即将跳转的网站url。
搜索引擎项目要实现的内容就是这三大块。
① 爬虫程序抓取网页信息:搜索引擎会使用爬虫程序在全网中抓取相关的HTML网页信息,并将其存储在服务器端的磁盘中(这里采用离线下载的方式获取网页信息)。
② 数据预处理:对抓取到的HTML文件进行去标签化和数据清理,即只保留网页文件中的主要信息(标题、正文、URL等)。
③ 建立索引:对预处理后的数据建立索引,以便快速检索。这里的索引包括正排索引和倒排索引。正排索引是根据文档ID查找文档内容,而倒排索引是根据文档内容查找文档ID(建立倒排索引要根据文档内容进行分词, 整理成不重复的多个关键字,再对应到相应的文档ID)。
④ 搜索查询:用户在浏览器中发起HTTP请求,服务端根据请求中的关键字在索引中查找相关文档,并将结果返回给客户端。
后端:C/C++(C++11), STL, 准标准库Boost, jsoncpp, cppjieba, cpp-httplib
前端:html, css, js, jQuery, Ajax
CentOS 7, vim, g++, Makefile, VSCode
boost官方文档: Boost C++ Libraries
下载文档: Index of main/release/1.82.0/source (jfrog.io)
boost库安装: sudo yum install -y boost-devel
cppjieba: GitHub - yanyiwu/cppjieba: "结巴"中文分词的C++版本
注: 安装后, 要将deps下的limonp拷贝一份放到include/cppjieba内
这个安装后可能存在deps下的limonp内无数据, 就要再安装一下limonp: GitHub - yanyiwu/limonp at a269e34dc4948d5a9209e21a7887b52daa0d3e78
安装后把limonp/include下的limonp拷贝到刚才安装的cppjieba的include/cppjieba中
安装方式:
cppjieba: git clone GitHub - yanyiwu/cppjieba: "结巴"中文分词的C++版本
limonp: git clone GitHub - yanyiwu/limonp: C++ headers(hpp) library with Python style.
使用cppjieba需要使用较新版本的gcc、g++, 可以自行搜索升级方式
jsoncpp安装: sudo yum install -y jsoncpp-devel
cpp-httplib: cpp-httplib: cpp-httplib - Gitee.com
cpp-httplib安装: git clone cpp-httplib: cpp-httplib
在搜索模块 searcher 中, 如果根据关键字的各个词检索查找时, 直接创建一个存储倒排拉链的vector数组, 并且在获得当前关键字的倒排拉链后直接插入到这个vector中, 那么就可能出现一个问题: 搜索结果出现重复文档的问题.
原因: 搜索关键字被jiaba分词后的几个词对应在同一个文档((即同一个doc_id))出现, 导致倒排拉链中存在重复情况. 进而出现了多个一样的搜索结果.
存在这种问题的搜索模块实现如下:
那么如何解决?
首先需要重新创建一个结构体InvertedElemPrint, 不能再使用之前的index的倒排索引的结构体, 而这个结构体中将原来的string类型的word, 变成了vector<string>类型, 这样这一个结构体如果遇到多个词对应在同一个文档(同一个doc_id)的情况下, 就可以把这多个词都插入到vector数组中.
接下来可以创建一个token_map哈希表, 用于doc_id与InvertedElemPrint建立映射关系, 目的是为了根据doc_id去重. 遍历根据doc_id获得到的倒排拉链, 然后创建或获得doc_id在哈希表中所映射的InvertedElemPrint, 然后将InvertedElem内的关键字word放入InvertedElemPrint的vector数组中. 这样即使有doc_id相同的关键字也都会放入同一个vector中, 完成去重的效果.
最后创建一个存储InvertedElemPrint的vector数组, 并把完成去重后的每一个不重复的doc_id倒排索引放入其中. 用于后面进行合并排序, 汇总查找结果, 并按照相关性权重(weight)降序排序.
解决问题后的搜索模块实现如下:
实现httplib的过程中遇到的问题可以说很多,究其原因是对TCP、HTTP、多路转接的理解不够深刻,同时使用经验较少,导致出现了很多的低级错误(包括请求与响应不符合http协议格式的低级错误)。
解决的方式也很简单,反复查看相关文档,多次理解相关协议与方案,编写代码并反复进行调试与修改。
文档的作用是为了显示出当前项目的运行情况,是否正常运行,同时也作为一种调试的手段,是很有作用的。
这里通过实现一个单例类,这个类将标准输出和标准错误的内容重定向到日志文件中,除了该类,又实现了一个日志函数,使用可变参数列表,用来接收不同的日志内容。调用上,只需要在项目执行前,调用类内的enable函数,然后在需要日志的位置,调用LogMessage函数,添加需要的日志即可。
(1)竞价排名定义
竞价排名是一种按效果付费的网络推广方式, 通过购买相关关键词, 然后给予出价获得竞价排名, 展现给目标用户, 从而获取用户点击.
很多浏览器都存在竞价排名, 因此这里我也实现一个简单的竞价排名.
(2)实现方法
首先创建一个文档, 把参与竞价排名的网址url以及出价用 | 分割放入advertise.txt文档中, 每个网址之间用 分隔.
然后在index中创建advertise_rec哈希表, 用于记录竞价信息. 通过AddAdvertise函数从advertise.txt文档中读取信息并插入到advertise_rec哈希表中. 再实现一个直接返回advertise_rec的函数, 用于将advertise_rec哈希表给到searcher中.
最后在searcher中先通过index的AddAdvertise函数获取竞价信息, 再获取advertise_rec哈希表, 然后当遍历倒排拉链, 把重复的doc_id合并实现后, 再一次遍历, 判断该关键字对应的文档是否参与竞价排名, 如果参与, 就修改其权重, 并且在title后面加上[广告]标识.
(3)实现
advertise.txt:
index:
searcher:
(4)测试结果
我们平常在搜索时,无论我们是否写了 "了", "的", "吗" 类似这样的字,搜索出来的结果是没有变化的,因为这些词在搜索中是没有什么作用的,我们想搜的内容是不会因为这些词而发生变化。而去掉这些暂停词之后,会使得搜索变得更快。这里我也实现了该功能。
通过在util中实现一个JiebaUtil单例类,在进行Jieba分词的过程中,通过将这些词与暂停词文档进行对比,如果是暂停词,就将该词去掉。
如果使用httplib库,那么http_server这一模块可以很轻松的完成,只需要调用其中的函数即可,但是这个httplib库在实际的公司项目中基本上是不会使用这个httplib库的,因为这个库是存在一些缺点的,可能会出现一些问题。
因此,这里我自己实现该项目所需用到的相关httplib中的接口,包括TCP和http的实现。
其中TCP是按照多路转接的方案进行实现的,用到了epoll。
以上就是本篇文章【search_engine:搜索引擎实现】的全部内容了,欢迎阅览 ! 文章地址:http://dfvalve.xrbh.cn/news/3105.html 资讯 企业新闻 行情 企业黄页 同类资讯 首页 网站地图 返回首页 迅博思语资讯移动站 http://keant.xrbh.cn/ , 查看更多