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 删除。