- 前言
- 一. 项目的相关背景
- 二. 搜索引擎的相关宏观原理
- 三. 搜索引擎技术栈和项目环境
- 四. 搜索引擎具体原理
- 4.1 正排索引
- 4.2 倒排索引
- 五. 编写数据去标签与数据清洗的模块Parser
- 5.1 数据导入
- 5.2 数据清理
- 5.2.1 处理Html文件与boost库的引用
- 5.2.2 代码结构
- 六. 编写建立索引的模块Index
- 6.1 Index基本结构
- 6.2 构建正排
- 6.3 构建倒排
- 6.3.1 倒排原理
- 6.3.2 cppjieba的下载与链接
- 6.3.3 cppjieba的使用
- 6.3.4 倒排索引代码
- 七. 编写搜索引擎模块Searcher
- 7.1 Jsoncpp的安装与基本使用
- 7.2 Searcher基本结构
- 八. 编写HTTP-Server模块
- 九. 编写前端模块
- 9.1 VSCode远程连接云服务器
- 9.2 编写前端代码
- 十. 优化
- 10.1 添加日志
- 10.2 挂起服务
- 十一. 效果
- 总结
前言
Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称。
Boost库由Boost社区组织开发、维护。其目的是为C++程序员提供免费、同行审查的、可移植的程序库。Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能。Boost库使用Boost License来授权使用,根据该协议,商业的非商业的使用都是允许并鼓励的。
一. 项目的相关背景
相较于对全网内容的搜索引擎,如某些互联网大型企业的搜索引擎,即:百度、谷歌、Mozilla火狐搜索引擎,我们想要自己实现是需要耗费极大的时间和物质精力的,因为这涉及我们将全网的相关网页信息抓取,以及将全网信息进行保存和建立相关的索引模块,且这些搜索引擎已经有了相当成熟的受众群体,再去实现的意义有待商榷。
Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。在C++社区中影响甚大,是不折不扣的“准”标准库。即便如此,Boost库的官方网站https://www.boost.org/ 内并没有对Boost库这一庞大库配置针对性搜索的相关的搜索引擎,截止实现该项目前的Boost官网中,Boost库的发行版本已至1.79.0,测试版本已至1.80.0,无论是什么版本,我们想了解Boost库中某一具体库的功能,都需要通过按照功能分类的Boost库列表,即相关版本的Documentation进行查找,我们可以通过实现一个Boost搜索引擎实现更优地解决这个问题。我们实现的是站内搜索,这使我们搜索的数据更垂直,相关度更高,数据量更小,针对性也就更强。
通过和目前主流搜索引擎搜索到的内容做对比,可以发现搜索的展现结果:网页的标题(title)、网页内容的摘要描述(content/desc)、即将跳转的网址url(url) 等,这也将是我们重点实现的。
二. 搜索引擎的相关宏观原理
站内搜索通俗来讲是一个网站或商城的“大门口”,一般在形式上包括两个要件:搜索入口和搜索结果页面,此处我们不用考虑爬取数据,因为我们的数据是从Boost官方网站中下载下来的,所以宏观上我们主要考虑客户端和服务器的交互即可。
三. 搜索引擎技术栈和项目环境
技术栈: C/C++/C++11,STL,准标准库Boost库,Jsoncpp,cppjieba,cpp-httplib
- 不用考虑从网站上爬取数据的过程,只考虑客户端和服务端要进行交互,于是我们采用Jsoncpp达到浏览器和服务器之间进行数据交换的目的
- 使用cppjieba将搜索关键字进行切分
项目环境: Centos7云服务器(Xshell7),vim/gcc(g++)/Makefile,VSCode
前端实现(选学): HTML5,CSS,JavaScript,jQuery,Ajax
四. 搜索引擎具体原理
后台架构上是比较复杂的,其核心要件包括:中文分词技术、页面抓取技术、建立索引、对搜索结果排序以及对搜索关键词的统计、分析、关联、推荐等。
4.1 正排索引正排索引:从文档ID找到文档内容(文档中的关键字)
假设此处有两个文档,如:
- 文档1:雷军买了四斤小米
- 文档2:雷军发布了小米手机
| 文档ID | 文档内容 |
|---|---|
| 1 | 雷军买了四斤小米 |
| 2 | 雷军发布了小米手机 |
目标文档分词:
目的: 方便建立倒排索引和查找
停止词: 的、了、a、the,一般我们在分词的时候可以不考虑
分词:
- 雷军买了四斤小米:雷军买四斤小米
- 雷军发布了小米手机:雷军发布小米手机
倒排索引:根据文档内容,分词,整理不重复的各个关键字,联系到对应文档ID
| 关键字(具有唯一性) | 文档ID,weight(权重) |
|---|---|
| 雷军 | 文档1、文档2 |
| 买 | 文档1 |
| 四斤 | 文档1 |
| 小米 | 文档1、文档2 |
| 四斤小米 | 文档1 |
| 发布 | 文档2 |
| 小米手机 | 文档2 |
模拟一次查找的过程:
-
给出要查找的关键字或具体内容 -> 小米手机
-
倒排索引中查找 ->
-
根据文档内容分词后的关键字提取出文档ID -> 文档2(2)
-
正排索引中查找 -> 2
-
根据文档ID找到文档内容 -> 雷军发布了小米手机
-
title+content/desc+url文档结果进行摘要 ->
-
构建相应结果
Boost官网:http://www.boost.org/
1. 从boost官网下载数据
此处我们下载的是最新发行版本1.79.0
2. 在我们的云服务器中打开
rz -E
3. 解压打开的boost文件
tar -xzf boost_1_79_0.tar.gz
4. 显示
所显示的内容就是我们在boost官网所看到的全部内容
5. 建立data/input,将boost文档下的html文件复制到input中,input用于存放数据源
//cd boost_searcher //cd doc //doc中存放着绝大多数的html文件 mkdir -p data/input//建立目录 cp -rf boost_1_79_0/doc/html/ * data/input/ //将doc下所有的网页内容html*拷贝到input下,input作为数据源
6. 此时boost文件已经用不到了,可以删除
rm boost_1_79_0/ -rf//rm -rf 命令意味着递归地、强制删除指定的目录 rm boost_1_79_0.tar.gz//删除压缩包
目前只需要boot_1_79_0/doc/html目录下的html文件,用它来建立索引
5.2 数据清理
首先在/root/boost_searcher下建立parser.cc文件,用来后续编写数据清理的代码;继续建立util.hpp作为接下来系列操作的工具包。
[root@192 boost_searcher]# touch parser.cc [root@192 boost_searcher]# touch util.hpp5.2.1 处理Html文件与boost库的引用
//原始数据->去标签之后的数据 <>:HTML的标签,这个标签对我们进行搜索是没有价值的,我们需要将他处理掉,标签又分为单标签和双标签,比较典型的单标签有,比较典型的双标签有、、 //代码: [root@192 data]# mkdir raw_html [root@192 data]# ll 总用量 16 drwxr-xr-x. 58 root root 12288 7月 22 10:51 input//这里放的是原始的html文档 drwxr-xr-x. 2 root root 6 7月 22 11:08 raw_html//这里放的是去标签之后的文档 [root@192 data]# ls -Rl | grep -E '*.html' | wc -l 8173
目标:
version1: 把每个文档都去标签,然后写入到同一个文件中,每个文档内容不需要n换行,文档和文档之间3区分:
类似于:XXXXXXXXX3YYYYYYYYYYY3ZZZZZZZZZZZZZZZ3
采用:
version2: 写入文件中,一定要考虑下一次在读取时,也要方便操作,即便于我们使用getline(ifstream,line)直接获取文档全部内容:title3content3url3
类似于:title3content3url n title3content3url n title3content3url n…
为什么要用3作为分隔符:这是因为文档中的每个字符实际上与ASCII表一一对应,ASCII表中又分为ASCII控制字符和ASCII打印字符,ASCII控制字符是不可显示的,因此,一般我们在文档中看到的字符都属于ASCII打印字符,3中的3对应Ctrl^C,字符解释为正文结束,同时它不会显示,也不会对我们去标签后的新的文档产生影响。
以上实现起来都需要我们对boost库的使用:
boost库的安装:
[root@192 data]# sudo yum install -y boost-devel //是boost的开发库
//使用boost库实现以3作为分隔符并进行压缩
class StringUtil{
public:
static void Split(const std::string &target,std::vector *out,const std::string &sep) //sep是切分符
{
//boost::split(vec,test,boost::is_any_of(","),boost::token_compress_on); vec是一个数组,test是一个字符串,is_any_of()里面是分隔符
//token_compress_on: aaa3bbb3333ccc3 即确定"bbb3"和"ccc3"之间的"333"需不需要压缩,默认是off,即不压缩
boost::split(*out,target,boost::is_any_of(sep),boost::token_compress_on);
}
};
ParseHtml(重点):
1. ParseTitle:
提取对应的title:
2.ParseContent:
去掉文档内容的标签: 在进行遍历时,只要碰到了" > ",就意味着当前标签被处理完毕,新的标签开始了。
3.ParseUrl:
构建url: boost库的官方文档,和我们下载下来的文档是有路径的对应关系的。
官网URL样例:https://www.boost.org/doc/libs/1_79_0/doc/html/accumulators.html
我们下载下来的URL样例:boost_1_79_0/doc/html/accumulators.html
我们拷贝到我们项目中的样例:data/input/accumuelators.html//我们把下载下来的boost库doc/html
*{
margin:0;
padding:0;
}
html,
body{
height: 100%;
}
.container{
width: 800px;
margin: 0px auto;
margin-top: 15px;
}
.container .search{
width: 100%;
height: 52px;
}
.container .search input{
float:left;
width: 600px;
height: 50px;
border: 1px solid black;
border-right: none;
padding-left: 10px;
color: #ccc;
font-size: 15px;
}
.container .search button{
float: left;
width: 150px;
height: 52px;
background-color:#fc5531;
color: #FFF;
font-size: 17px;
font-family:'Courier New', Courier, monospace;
}
.container .result{
width: 100%;
}
.container .result .item{
margin-top: 15px;
}
.container .result .item a{
display: block;
text-decoration: none;
font-size: 20px;
color: #4e6ef2;
}
.container .result .item a:hover{
text-decoration: underline;
}
.container .result .item p{
margin-top: 4px;
display: block;
font-size: 17px;
font-family:Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
}
.container .result .item i{
display: block;
font-size: 14px;
font-style: normal;
color: green;
}
编写JS:
如果直接使用原生的JS,成本会比较高(xml),推荐使用JQuery。十. 优化 10.1 添加日志
#pragma once #include10.2 挂起服务#include #include #define NORMAL 1 #define WARNING 2 #define DEBUG 3 #define FATAL 4 #define LOG(LEVEL,MESSAGE) log(#LEVEL,MESSAGE,__FILE__,__LINE__) void log(std::string level, std::string message, std::string file, int line) { std::cout<< "[" << level << "]" << "[" << time(nullptr) << "]" << "[" << message << "]" << "[" << file << ":" << line << "]" << std::endl; }
使用挂起服务可以帮助我们即使在没有运行./http_server的情况下,依旧可以建立客户端与服务器的连接,继续使用我们的搜索引擎。
部署挂起服务:
nohup ./http_server > log.txt 2>&1 &
终止挂起服务:
kill + 进程号十一. 效果 总结
//原创文章,请勿转载以及为个人使用。 //源代码地址 https://github.com/Ph4rynx/BOOST_SEARCHER



