首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Boost搜索引擎项目】构建Boost站内搜索引擎的技术

【Boost搜索引擎项目】构建Boost站内搜索引擎的技术

原创
作者头像
小焱
发布2025-07-06 19:30:44
发布2025-07-06 19:30:44
770
举报
文章被收录于专栏:Java开发Java开发

1、项目的相关背景

公司:百度、搜狗、360搜索、头条新闻客户端,我们自己实现是不可能的!

站内搜索:搜索的数据更垂直,数据量其实更小

boost的官网是没有站内搜索的,需要我们自己做⼀个

boost网站中是没有相关的搜索引擎的,我们自己实现一个!

boost 官网: https://www.boost.org/

我们使用最新的boost_1_86_0/doc/html⽬录下的html⽂件,⽤它来进⾏建⽴索引

2.搜索引擎的相关宏观原理

3.搜索引擎技术栈和项目环境

技术栈:C/C++ C++11, STL, 标准库Boost,Jsoncpp,cppjieba,cpp-httplib ,

选学: html5,css,js、jQuery、Ajax(前端)

项目环境: Ubuntu 9.4.0云服务器,vim/gcc(g++)/Makefile ,vs2022 or vscode

4.正排索引vs倒排索引-搜索引擎具体原理

文档1:雷军买了四斤小米

文档2:雷军发布了小米手机

正排索引:就是从⽂档ID找到⽂档内容(⽂档内的关键字)

目标文档进行分词(目的:方便建立倒排索引和查找):

文档1[雷军买了四斤小米]: 雷军/买/四斤/小米/四斤小米

文档2[雷军发布了小米手机]:雷军/发布/小米/小米手机

停止词:了,的,吗,a,the,一般我们在分词的时候可以不考虑

倒排索引:根据文档内容,分词,整理不重复的各个关键字,对应联系到文档ID的方案。

模拟一次查找的过程:

用户输入:小米 ->倒排索引中查找->提取出文档ID(1,2)->根据正排索引->找到文档的内容 ->

title+conent(desc)+url 文档结果进行摘要->构建响应结果

倒排->正排->文档摘要

5.编写数据去标签与数据清洗的模块 Parser

5.1.去标签

我们首先需要将boost网站里的站内资源进行下载,并压缩到我们的项目当中,作为初始数据进行保存

什么是标签?

<> : html的标签,这个标签对我们进⾏搜索是没有价值的,需要去掉这些标签,⼀般标签都是成 对出现的!

为什么要去标签?我们随便打开一个压缩好的网页资源,他是这样的:

大部分内容其实都是标签,对我们进行搜索是没有用的,所以我们要进行去标签。

目标:

把每个文档都去标签,然后写入到同一个文件中!每个文档内容不需要任何\n!文档和文档之间用\3 区分

parser文件的编写

5.2.代码的整体框架:

#include <iostream>

#include <string>

#include <vector>

#include<boost/filesystem.hpp>

#include"util.hpp"

const std::string src_path = "data/input/"; //这⾥放的是原始的html⽂档

const std::string output = "data/raw_html/raw.txt";//这是放的是去标签之后的⼲净⽂档

typedef struct DocInfo

{

std::string title; // 文档的标题

std::string contnt; // 文档内容

std::string url; // 该文档在官网中的url

} DocInfo_t;

// const &: 输⼊

//*: 输出

//&:输⼊输出

bool EnumFile(const std::string &src_path, std::vector<std::string>*files_list);

bool ParseHtml(const std::vector<std::string> &files_list,std::vector<DocInfo_t> *results);

bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output);

int main()

{

std::vector<std::string> files_list;

// 第⼀步: 递归式的把每个html⽂件名带路径,保存到files_list中,⽅便后期进⾏⼀个⼀个的⽂件进⾏读取

if (!EnumFile(src_path, &files_list))

{

std::cerr << "enum file name error!" << std::endl;

return 1;

}

// 第⼆步: 按照files_list读取每个⽂件的内容,并进⾏解析

std::vector<DocInfo_t> results;

if (!ParseHtml(files_list, &results))

{

std::cerr << "parse html error" << std::endl;

return 2;

}

// 第三步: 把解析完毕的各个⽂件内容,写⼊到output,按照\3作为每个⽂档的分割符

if (!SaveHtml(results, output))

{

std::cerr << "sava html error" << std::endl;

return 3;

}

return 0;

}

AI写代码

cpp

运行

EnumFile函数的实现:

bool EnumFile(const std::string &src_path, std::vector<std::string>*files_list)

{

namespace fs = boost::filesystem;

fs::path root_path(src_path);

//判断路径是否存在,不存在,就没有必要往后走了

if(!fs::exists(root_path))

{

std::cerr << src_path << "not exists" << std::endl;

return false;

}

//定义一个空的迭代器,用来判断递归结束

fs::recursive_directory_iterator end;

for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)

{

//判断文件是否是普通文件,html都是普通文件

if(!fs::is_regular_file(*iter))

{

continue;

}

if(iter->path().extension() != ".html")//判断文件名的后缀是否符合要求

{

continue;

}

//std::cout << "debug: " << iter->path().string() << std::endl;

//当前的路径一定是一个合法的,以.html结束的普通网页文件

files_list->push_back(iter->path().string());//将所有带路径的html保存在file_list,方便后续进行文本分析

}

return true;

}

AI写代码

cpp

运行

EnumFile测试结果

如下,可以把所有的.html网页输出出来了

我们提取网页中的title和content都比较简单。

提取title是直接在网页内容中查找<title>,然后进行字符串的截取即可。

bool ParseTitle(const std::string &file, std::string *title)

{

std::size_t begin = file.find("<title>");

if(begin == std::string::npos)

{

return false;

}

std::size_t end = file.find("</title>");

if(end == std::string::npos)

{

return false;

}

begin += std::string("<title>").size();

if(begin > end)

{

return false;

}

*title = file.substr(begin, end - begin);

return true;

}

AI写代码

cpp

运行

提取content就是一个去标签的过程,我们这里采用的是基于简单的状态机进行去标签。

bool ParseContent(const std::string &file, std::string *content)

{

//去标签,基于一个简易的状态机

enum status

{

LABLE,

CONTENT

};

enum status s = LABLE;

for(char c : file)

{

switch(s)

{

case LABLE:

if(c == '>') s = CONTENT;

break;

case CONTENT:

if(c == '<') s = LABLE;

else

{

//我们不想保留原始文件中的\n,因为我们想用\n作为html解析之后文本的分隔符

if(c == '\n') c = ' ';

content->push_back(c);

}

break;

default:

break;

}

}

return true;

}

AI写代码

cpp

运行

如何提取网页的url呢?

boost库的官方文档,和我们下载下来的文档,是有路径的对应关系的

官网URL样例:

https://www.boost.org/doc/libs/1_86_0/doc/html/accumulators.html

我们下载下来的url样例:boost/1_86_0/doc/html/accumulators.html

我们拷贝到我们项目中的样例:data/input/accumulators.html //我们把下载下来的boost库doc/html/* copy data/input/

url head ="https://www.boost.org/doc/libs/1_86_0/doc/html";

url tail = [data/input](删除)/accumulators.html -> url tail =/accumulators.html

url = url_head + url_tail ;相当于形成了一个官网链接!

bool ParseUrl(const std::string &file_path ,std::string *url)

{

std::string url_head = "https://www.boost.org/doc/libs/1_86_0/doc/html/";

std::string url_tail = file_path.substr(src_path.size());

*url = url_head + url_tail;

return true;

}

AI写代码

cpp

运行

将解析内容写入文件中:

bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)

{

#define SEP '\3'

//按照二进制方式进行写入

std::ofstream out (output, std::ios::out | std::ios::binary);

if(!out.is_open())

{

std::cerr << "open " << output << " failed!" << std::endl;

return false;

}

//可以开始进行文件内容的写入了

for(auto &item : results)

{

std::string out_string;

out_string = item.title;

out_string += SEP;

out_string += item.contnt;

out_string += SEP;

out_string += item.url;

out_string += '\n';

out.write(out_string.c_str(), out_string.size());

}

return true;

}

AI写代码

cpp

运行

测试解析网页title,content,url是否正确?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档