重庆旅游seo整站优化,素材,比较好的海报设计网站,网页设计制作方法文章目录 1.背景2.宏观原理3.相关技术与开发环境4. 实现原理1.下载2.加载与解析文件2.1获取指定目录下的所有网页文件2.2. 获取网页文件中的关键信息2.3. 对读取文件进行保存 3.索引3.1正排与倒排3.2获取正排和倒排索引3.3建立索引3.3.1正排索引3.3.2倒排索引 4.搜索4.1 初始化… 文章目录 1.背景2.宏观原理3.相关技术与开发环境4. 实现原理1.下载2.加载与解析文件2.1获取指定目录下的所有网页文件2.2. 获取网页文件中的关键信息2.3. 对读取文件进行保存 3.索引3.1正排与倒排3.2获取正排和倒排索引3.3建立索引3.3.1正排索引3.3.2倒排索引 4.搜索4.1 初始化4.2 搜索功能 5. http_server5.1 升级gcc5.2 安装cpp-httplib5.3编写http_server.cc 6. 编写前端模块6.1 HTML6.2 CSS6.4 JavaScript 总结 与 拓展尾序 效果图 项目源码链接 1.背景 百度360谷歌等搜索引擎的实现门槛过高几乎不可能由个人进行实现。站内搜索其中的资源相对比较垂直适合个人进行实现并借此达到管中窥豹的效果。Boost库是没有站内搜索的实现更有意义。 搜索相关内容
共性都含有标题摘要网站的网址。 说明有些网站还有图片广告等信息但由于我们做的是Boost库的搜索这些信息知道即可。 2.宏观原理 3.相关技术与开发环境
技术栈C/CC11STLJsonCppBoostCpp-HttplibJquery正排与倒排索引。开发环境: Centos云服务器vim/g/gcc/MakefileVsDode/VS2019。
4. 实现原理 说明 为了方便理解代码博主将项目的目录进行贴出因为下面我们include包含使用的库使用的是绝对路径。 项目的路径为/home/shun_hua/practical-projects/Boost_Search 原因相对目录是基于进程的工作目录进程的工作目录在项目的路径下而我们写的代码是在项目的子目录的路径下因此要么把所有的文件都放在项目的路径下要么就用绝对路径虽然长但是可以把文件整理分类看着比较简洁。 1.下载 进入boost官网点击进入 第一步 第二步 第三步下载完成之后在对应的Linux操作系统系统上输入上传文件进行上传。
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$rz -E
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ ls
boost_1_84_0.tar.gz
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ tar xzf boost*
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ ls
boost_1_84_0 boost_1_84_0.tar.gz说明 rz - E是上传较大文件时进行使用。tar -xzf 是对文件进行解压缩。boost_1_84_0是解压缩之后的文件。安装rz : sudo yum install -y rz 第四步找到解压缩文件中的html提取出来用于作为搜索引擎的数据。 路径/boost_1_84_0/doc/html [shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ ls
boost_1_84_0
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ cp -r ./boost_1_84_0/doc/html ./input
[shun_huaiZ2zebfc5jur5cm0zu2n3gZ Boost_Search]$ ls
boost_1_84_0 input说明cp -r [指定路径的目录] [目标路径 重命名] [shun_huaiZ2zebfc5jur5cm0zu2n3gZ input]$ ls -R | grep -E .html | wc -l
8586说明: ls -R 显示所有文件目录递归显示所有文件。grep -E [字符串] [文件] 显示出带有指定字符的信息。wc -l [文件] 显示出文件的行数。 2.加载与解析文件
基本框架 2.1获取指定目录下的所有网页文件 引入文件库Boost文件库具体使用里面的filesystem里面的接口。 接口文档 命名空间boost::filesystem 类class path string string(const codecvt_type cvtcodecvt()) const; ————将path对象转换为string类。
path extension() const; ——文件的后缀。迭代器class recursive_directory_iterator recursive_directory_iterator() noexcept;//默认构造其实执向的是end
explicit recursive_directory_iterator(const path p, directory_options opts\ directory_options::none);//用根目录初始化即整个多叉树根。接口 bool is_regular_file(const path p); //判断是否是普通的文件目录不是普通文件。
bool exists(const path p); //判断是否文件的目录是否存在。实现代码:
bool GetPathFiles(const string path,vectorstring* files)
{//首先将path转化为boost库的path便于处理//防止命名污染的情况namespace fs boost::filesystem;fs::path root_path(path);if(!exists(root_path)){lg(CRIT,path is not exist!);return false;}fs::recursive_directory_iterator end;for(fs::recursive_directory_iterator cur(root_path); cur ! end; cur){//如果不是普通文件例如目录。if(!is_regular_file(cur-path())){continue;}string suffix cur-path().extension().string();if(suffix ! .html){continue;}string path cur-path().string();// cout path endl;files-push_back(cur-path().string());}return true;
}2.2. 获取网页文件中的关键信息
核心
标题——title内容——content网址——url
网页的大致内容
说明 …. 之间的为标题即网页窗口显示的内容。除去… 之间的内容其余的都为内容。网址根据网页的基本内容结合本地的相对目录获取到具体boost库的网址。 解析文件的基本流程
读取网页文件的内容
//使用命名空间避免命名污染
namespace util
{namespace filesystem{bool ReadFiles(const std::string file_path,std::string *text){std::ifstream fin(file_path);if(!fin.is_open()){lg(WARNNING,open file fail!);return false;}string line;while(getline(fin,line)){*text line; }return true;}}
}获取标题
bool PraseTile(const string text,string *title)
{string prefix title;string suffix /title;auto begin text.find(prefix);auto end text.find(suffix);if(begin string::npos || end string::npos) return false;begin prefix.size();*title text.substr(begin,end - begin);return true;
}获取内容 此处采用的是状态机的实现方式即除了…. 都是内容。
bool PraseContent(const string text,string *content)
{State s LABLE;for(char ch : text){switch (s){case LABLE:if(ch )s CONTENT;break;case CONTENT: if(ch ){s LABLE;break;}//把换行符去掉。*content ch \n ? : ch;}}return true;
}解析网站的url 实现原理 我们用的是/doc/html下的所有*.html文件。因此网站的前缀为https://www.boost.org/doc/libs/1_84_0/doc/html/根据对应的*.html文件再去掉本地目录的前缀 /home/shun_hua/practical-projects/Boost_Search得到资源的后缀。前缀与后缀拼接出来的结果即为搜索网站的Url。 实现代码 bool ParseUrl(const string path,string* url){string url_head https://www.boost.org/doc/libs/1_84_0/doc/html;// /doc/html文件中存放的是帮助文档的html用于搜索引擎的查找。// 其它目录下的html先暂时不做考虑。auto pos path.find(src_path);if(pos string::npos) return false;pos src_path.size();string url_tail path.substr(pos);*url url_head url_tail;return true;}因此我们的解析网页的实现代码为
bool PraseHtmls(const vectorstring files,vectorHtmlInfor* contents)
{//先读取文件的内容int cnt 5;for(auto file_path : files){//1.打开文件读取对应的内容。string text;namespace u_fs util::filesystem;if(!u_fs::ReadFiles(file_path,text)){lg(WARNNING,read files content fail!);continue;}//2.解析网站的标题HtmlInfor htm;if(!PraseTile(text,htm.title)){lg(WARNNING,Prase html title content fail!);continue;}//3.解析网站的内容if(!PraseContent(text,htm.content)){lg(WARNNING,parse content fail!);continue;}if(!ParseUrl(file_path,htm.url)){lg(WARNNING,parse url fail!);continue;}//for debug:// cout htm.title endl;// cout htm.url endl;// cout htm.content endl;// break;contents-push_back(htm);}return true;
}2.3. 对读取文件进行保存
保存信息的结构体为
struct HtmlInfor
{string title;string content;string url;
};保存方式 在进行读取时我们希望一次能读取一个文件的内容。因此文件与文件之间用 ‘\n’ 进行划分。在分析一个文件的内容时我们需要获取到标题内容url信息因此这之间需要用一个控制字符 ‘\3’划分即可。 实现代码
bool SaveHtmls(const vectorHtmlInfor contents,const string path)
{std::fstream out(path,ios_base::binary | ios_base::out);if(!out.is_open()){lg(ERRO,open file %s fail!,path.c_str());return false;}int rate;int cur 0;for(const HtmlInfor infor : contents){rate 100 * (cur) / contents.size();processbar(rate);string mes infor.title \3 infor.url \\3 infor.content \n;out.write(mes.c_str(),mes.size()); }return true;
}此处用之前学到的进度条程序显示保存文件的进度实现可视化。 进度条代码
#pragma once
#includestdio.h
#includeunistd.h
#includestring.h
#define MAX 102
#define STYLE #
#define RIGHT
typedef void (*call_back)(int);
void processbar(int rate);
void init();
char buf[MAX];
char signal[5] {-,\\,|,/,\0};
void init()
{memset(buf,\0,sizeof(buf));
}
void processbar(int rate)
{if(rate 100 || rate 0){return;}int len strlen(signal);if(rate 100){printf([%-100s][%-3d%%] sourse load done!\r\n,buf,rate);usleep(1000);buf[rate] STYLE;return;}printf([%-100s][%-3d%%][%c]\r,buf,rate,signal[rate%len]);usleep(10);//刷新缓冲区fflush(stdout);//更新存储的进度条buf[rate] STYLE;if(rate 100){buf[rate] RIGHT;}
}3.索引
3.1正排与倒排 采用技术正排索引与倒排索引。 举例 假设针对如下三个标题建立正排和倒排索引。 乔布斯买苹果手机。 乔布斯吃苹果。 乔布斯看手机。
建立正排索引的文档
文档ID文档内容1乔布斯买苹果手机。2乔布斯吃苹果。3乔布斯看手机。
提取关键字 乔布斯 苹果手机 苹果 手机 买 吃 看 根据关键字建立倒排索引
关键字文档ID乔布斯1,2,3苹果手机1苹果2手机13吃2看3买1
总结一下
正排索引即对读取的文件内容进行编号。倒排索引是针对关键字找到文件的编号从而找到文件的内容。 基本框架
typedef std::vectorint Interved_List;
//正排索引
struct DocInfor
{DocInfor(){}DocInfor(int Idx,string Title,string Url,string Content):idx(Idx),title(Title),url(Url),content(Content){};int idx;//文档IDstd::string title;std::string url; std::string content;
};
//索引类实现正排和倒排索引
class Index
{//Document
public:Index(){}~Index(){}DocInfor* GetDocInfor(int doc_idx);Interved_List* GetInterList(const string keyword);//从文件中读取内容建立正排和倒排索引bool BuildForwardIndex(const string path);
private://这里的vector的数组的下标天然就可以当做DocInfor的idxstd::vectorDocInfor forward_index;//正排索引std::unordered_mapstd::string,Interved_List interved_index;//倒排索引
};3.2获取正排和倒排索引 正排索引根据文档ID进行获取倒排索引根据关键词获取对应的倒排拉链。 实现代码
DocInfor* GetDocInfor(int doc_idx)
{if(doc_idx forward_index.size()){lg(ERRO,doc_idx:%d,out of range.,doc_idx);return nullptr;}return forward_index[doc_idx];
}
Interved_List* GetInterList(const string str)
{auto it interved_index.find(str);if(it interved_index.end()){lg(ERRO,keyword is not exist.);return nullptr;}return it-second;
}3.3建立索引
3.3.1正排索引 说明: 我们可以采用string 容器的 find 与 substr接口实现文档内容的截取但是基于学习Boost文件库的目的这里直接使用现成的截取文档的接口。 接口
// In header: boost/algorithm/string/string.hpp//函数声明
templatetypename SequenceSequenceT, typename RangeT, typename PredicateT SequenceSequenceT split(SequenceSequenceT Result, RangeT Input, PredicateT Pred, token_compress_mode_type eCompress token_compress_off);/*
参数1:vectortype类型的用于存放切割后的内容。2切割的内容。3分割符。4切割的模式一般设置为token_compress_on,意为将连续的分割符看成一个。
*///例
#includeiostream
#includeboost/algorithm/string.hpp
#includestring
#includevector
int main()
{std::vectorstd::string res;std::string text aaaaaaaa\3\3bbbbbbbbbbb\3cccccccccc;std::string split_str \3;boost::split(res,text,boost::is_any_of(split_str),boost::token_compress_on);for(auto str : res){std::cout str std::endl;}return 0;
}
/*
output:aaaaaaaa bbbbbbbbbbbcccccccccc
*/接口
static DocInfor* GetForwardIndex(const string split,const string line,\vectorDocInfor* forward_index)
{vectorstring tmp;boost::split(tmp,line,boost::is_any_of(split),boost::token_compress_on);if(tmp.size() ! 3){lg(ERRO,split fail:GetForwardIndex);return nullptr;}forward_index-push_back(DocInfor(forward_index-\size(),move(tmp[0]),move(tmp[1]),move(tmp[2])));return forward_index-back();
}//说明此函数封装在命名空间util的struct String内3.3.2倒排索引
建立倒排的索引的结构体对象。如文档ID关键词相关系数。
struct InterElem
{//默认构造InterElem(){}//写了构造编译器就不会自动生成默认构造函数。InterElem(int id,std::string key,int rate):idx(id),word(key),weight(rate){}int idx;//文档idstd::string word;//关键词int weight;//权重
};对文档内容和标题进行分词。 说明倒排需要对内容进行分词而分词的工作有现成的库因此我们采用jieba库分词即可。 安装jieba工具 网址链接jieba分词 使用git clone https://gitcode.com/yanyiwu/cppjieba.git克隆到本地。 将库进行调整使用cp命令将cppjieba/deps/limonp 拷贝到 cppjieba/include/jieba 目录下。 将cppjieba/test/demo.cpp拷贝到与cppjieba同级目录下进行测试。 在与cppjieba同级目录下建立软连接ln -s cppjieba/include/jieba jiebaln -s cppjieba/dict dict
测试代码
#include jieba/Jieba.hpp
using namespace std;
std::string prefix /home/shun_hua/practical-projects/Boost_Search/Modules/Utils/;
std::string DICT_PATH prefix dict/jieba.dict.utf8;
std::string HMM_PATH prefix dict/hmm_model.utf8;
std::string USER_DICT_PATH prefix dict/user.dict.utf8;
std::string IDF_PATH prefix dict/idf.utf8;
std::string STOP_WORD_PATH prefix dict/stop_words.utf8;
int main()
{cppjieba::Jieba jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH);vectorstring words;vectorcppjieba::Word jiebawords;string s;string result;s 小明硕士毕业于中国科学院计算所后在日本京都大学深造;cout s endl;jieba.CutForSearch(s, words);cout [demo] CutForSearch endl;cout limonp::Join(words.begin(), words.end(), /) endl;
}说明我们的目的是针对关键词进行搜索因此使用CutForSearch接口即可。 我们用类进行封装
#include jieba/Jieba.hpp
std::string prefix /home/shun_hua/practical-projects/Boost_Search/Modules/Utils/;
std::string DICT_PATH prefix dict/jieba.dict.utf8;
std::string HMM_PATH prefix dict/hmm_model.utf8;
std::string USER_DICT_PATH prefix dict/user.dict.utf8;
std::string IDF_PATH prefix dict/idf.utf8;
std::string STOP_WORD_PATH prefix dict/stop_words.utf8;
//这里的JieBa类是用于封装的而cppjieba::Jieba是一个类型请注意进行区分。
struct JieBa
{private:static cppjieba::Jieba jieba;public:static void CutString(const std::string content,std::vectorstd::string words){jieba.CutForSearch(content,words);}
};
cppjieba::Jieba JieBa::jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH);补充中文和英文的分词jieba都支持博主已经测试过。 对关键词次数进行分析。对标题和内容都要进行分析。
struct Word_Cnt
{int title_cnt 0;//标题中关键词的出现次数。int content_cnt 0;//内容中关键词的出现次数。
};计算相关系数将获取到完整的InterElem元素打散到倒排索引中。 说明实际相关系数要考虑到多个维度且要基于数据进行分析这里我们不做那么复杂使用标题与内容中关键词的出现次数进行分析即可。 系数公式N*word_cnt.title_cnt M * word.content_cnt, 这里的N, M姑且就分别设置为10 与 1即标题的相关性占比较大。 细节 这里我们在对网页去标签时内容中也含有标签因此标签中的关键字在内容中被重复计算了一次。 jieba分词可能没有将我们所搜索的关键词在内容中分出来因此可能会跟实际有一点点偏差比实际的小一点。 实现代码
void GenerateIntervedIndex(const DocInfor doc)
{// 首先文档id是已经有的。// 剩余需要的是// 1.分析出关键词。std::vectorstd::string title_words;std::vectorstd::string content_words;// 进行jieba分词.....util::JieBa::CutString(doc.title, title_words);util::JieBa::CutString(doc.content, content_words);std::unordered_mapstd::string, Word_Cnt kv;// 2.记录关键词的出现次数。// 注意在实际搜索的过程中是忽略大小写的因此的对标题和内容都忽略大小写。for (auto word : title_words){boost::to_lower(word);kv[word].title_cnt;}for (auto word : content_words){boost::to_lower(word);kv[word].content_cnt;}// 3.计算相关系数打散到interverd_index中。for (auto it : kv){auto key_word it.first;auto cnt it.second;interved_index[key_word].push_back({doc.idx, move(key_word),\cnt.content_cnt 10 * cnt.title_cnt});}
}说明 在实际的搜索过程中是忽略大小写的因此这里我们采用了boost库中的to_lower统一转换为小写。由于to_lower会对传进去的内容本身进行修改因此使用范围for时应使用拷贝不可使用引用获取。我们并不需要对文档内容进行大小写转换只需要将索引的关键词和搜索的关键词进行大小写转化即可完成忽略大小写。 索引构建代码
bool BuildIndex(const string path)
{std::ifstream in(path, std::ios_base::in | std::ios_base::binary);if (!in.is_open()){lg(ERRO, open file fail,path is %s., path);return false;}string line;const int sum 8586;int cnt 0;// getline的默认分割符为\nstring split_str \3;while (getline(in, line)){cnt;processbar(cnt * 100 / sum );// 获取正排索引DocInfor *doc GetForwardIndex(split_str,line);if (doc nullptr){lg(WARNNING, doc is not exist.);continue;}// 获取倒排索引GenerateIntervedIndex(*doc);}return true;
} 说明 文档在建立时可能会比较慢因此写了一个进度条实现进度可视化。 sum为读取文件的数目如果处理文件的方式不变的话一般sum的值是不变的。 4.搜索
基本框架
#pragma once
#include ../Index/index.hpp
#include jsoncpp/json/json.h
namespace bs_search
{const string data_path /home/shun_hua/practical-\projects/Boost_Search/DataSource/output/data_processed.txt;class Searcher{private:bs_index::Index *index;public:Searcher(){}~Searcher(){}void InitSeacher(){//1.对单例index进行获取。//2.对index进行构建。}//对查询进行搜索void Search(const string query,string* json_str){//1.分词即对query进行分词便于查询。//2.根据关键词在倒排索引中进行查找。//3.根据相关系数即weight对查找的内容进行降序排序。//4.根据排序之后的结果构建对应的字符串。}};
} 说明 因为索引的构建的文件过大如果反复进行过程较慢因此我们这里的使用指针的形式并且将Index类设为单例(懒汉)。我们输入的查询语句也需要进行分词下面博主贴一个例子进行举例。且分词之后的结果需要进行大小写转换与倒排索引对应。我们查询之后的结果使用现成的json串内容基本情况的获取并且由于由于内容过大返回有关键词的摘要即可。 补充Index类
单例模式
static Index *instance()
{if (index_ptr nullptr){mtx.lock();if (index_ptr nullptr){index_ptr new Index();}mtx.unlock();}return index_ptr;
}说明 index_ptr 与 mtx的类型分别为Index * 与 mutex——C的锁头文件为mutex。都需要设置为静态变量便于进行外部通过类域进行获取。这里外面的第一层的if是为了提高并发度 因为多线程访问大多数情况是index不为空的情况。 获取关键描述
实现代码
std::string GetDesc(const std::string content,\
const std:: string word)
{ auto pos content.find(word);if(pos std::string::npos){return word is not in content;}//返回里面含有关键词的描述。int presize 50,sufsize 50;//获取pos前50个字节和后50个字节当做内容的描述int pre pos - presize,suf pos sufsize;int begin pre 0 ? pre : 0;int end suf content.size() ? suf : content.size();//返回摘要即可。return content.substr(begin,end - begin 1);
}说明 这个函数我设置的很简单只需要对内容进行搜索关键词的位置返回附近的内容即可如果没有我们设置一个默认值进行返回即可。 4.1 初始化
Index类提供了 instance 和 BuildIndex进行初始化因此直接调用接口即可。
实现代码
void InitSeacher()
{//1.对单例index进行获取。index bs_index::Index::instance();//2.对index进行构建。index-BuildIndex(data_path);
}4.2 搜索功能
对查询进行分词。 说明 索引的句子也进行了分词使用关键词查找并呈现对应的内容。在之前我们在 util::Jieba 即命名空间的对应类域中使用了jieba库的封装的接口CutString。 获取倒排索引。 在Index我们内含成员存有正排和倒排索引并实现了对应的接口——GetInterList直接用即可。 注意对分词的结果也要忽略大小写即为了与上面的索引模块对应统一转换为小写即可。说明: 转换为小写使用boost库中的to_lower接口即可。
降序排序 对获取的倒排索引进行降序排序使用algorithm 库里的sort使用lambda表达式自定义对应的排序规则即可。 说明自定义对象为我们Index中实现的struct InterElem
struct InterElem
{// 默认构造InterElem(){}// 写了构造编译器就不会自动生成默认构造函数。InterElem(int id, std::string key, int rate): idx(id), word(key), weight(rate){}int idx; // 文档idstd::string word; // 关键词int weight; // 权重
};获取文档内容封装为Json串
demo:
#includeiostream
#includejsoncpp/json/json.h
#includestring
int main()
{ int age 18; std::string name Shun_Hua; int id 12314213; Json::Value root; Json::StyledWriter wri; root[age] age; root[name] name; root[id] id; std::string json_str wri.write(root);std::cout json_str std::endl;return 0;
}说明 g 编译时需要用 -l 选项包含对应的库名。编译指令g json_demo.cc -stdc11 -ljsoncppCenOs按照json库的指令sudo yum install -y jsoncpp-devel 执行结果 搜索功能实现代码
void Search(const string query,string* json_str)
{//1.分词即对query进行分词便于查询。std::vectorstring words;util::JieBa::CutString(query,words);//2.根据关键词在倒排索引中进行查找。//获取与关键词相关的所有倒排拉链bs_index::Interved_List lists;for(auto word : words){//在此之前我们需要对关键词进行大小写转换boost::to_lower(word);bs_index::Interved_List* list index-GetInterList(word);if(nullptr list){continue;}//说明:这里的内容可能会有大量的重复最后可以保留较大的权值的文档ID进行排序。//template class InputIterator//void insert (iterator position, InputIterator first, InputIterator last);//在指定的迭代器位置插入对应的容器的迭代器区间。lists.insert(lists.end(),list-begin(),list-end());}//将list的文档ID可能会有大量的重复我们采用unordered_map进行去重用文档ID作为索引值//保留权值较高的元素或者将权值进行累加即可。std::unordered_mapint,bs_index::InterElem kv;for(auto elem : lists){int id elem.idx;if(kv.count(id)){//保留文档权值较高的即可。//kv[id] elem.weight kv[id].weight ? elem : kv[id];//对权值累加。kv[id] elem.weight;}else{kv[id] elem;}}bs_index::Interved_List Deduplication;for(auto pair : kv){Deduplication.push_back(std::move(pair.second));}//去重之后的结果进行赋值。lists move(Deduplication);//3.根据相关系数即weight对查找的内容进行降序排序。sort(lists.begin(),lists.end(),[](const bs_index::InterElem x,\const bs_index::InterElem y){return x.weight y.weight;});//4.根据排序之后的结果构建对应的字符串。//是根据对应的索引内容进行构建的。Json::Value root;Json::StyledWriter write;//这里我们还可以用Json::FastWriter writer; 没有上面那一种美观。for(auto interved : lists){bs_index::DocInfor* doc index-GetDocInfor(interved.idx);Json::Value val;val[title] doc-title;val[url] doc-url;val[content] index-GetDesc(doc-content,interved.word);//追加在root后面。root.append(val);}*json_str write.write(root);
}说明去重时我们采用unordered_mapint,InterElem相同文档ID时保留权值较大的文档即可便于后面的排序另外这里我们再赋值时使用move减少拷贝的次数。 测试 编写索引与搜索模块完毕我们创建一个search.cc文件进行测试。 #includeiostream
#includesearch.hpp
int main()
{ //获取搜索服务的对象。 bs_search::Searcher sear; sear.InitSeacher(); std::string query; while(true) { cout Please Enter query; std::getline(std::cin,query); string json_str; sear.Search(query,json_str); cout json_str endl; } return 0;
}效果 说明由于查询的关键词反馈的内容可能过多此处就不再显示了。 5. http_server
5.1 升级gcc 原因CentOs 7 的默认版本较老使用http_server的库会编译出错因此需要对gcc/g进行升级。 查看gcc/g 版本gcc -v 说明升级到7以上的版本即可。 安装扩展源scl
sudo yum install -y http://mirror.centos.org/centos/7/extras/x86_64/Packages/centos-release-scl-rh-2-3.el7.centos.noarch.rpm说明 可用su命令输入root密码切换至root进行安装。 普通用户添加至信任白名单后, 输入用户密码进行安装。具体操作链接详见文章开头 安装devtoolset
sudo yum install devtoolset-9-gcc-c说明: 这里的 -9 意为按照 g/gcc 9的版本这里我们使用7以上的即可。 激活devtooset
scl enable devtoolset-9 bash说明 这里的-9与第二步的意思相同你上一步输入了几这里就还输入几。在每次启动会话时版本就回退到原先的所以我们还需要输入为了避免重复输入我们可以放在用户对应的配置文件中。 具体步骤 命令行输入vim ~/.bash_profile。将激活代码贴到最后一行标好注释即可。 5.2 安装cpp-httplib
安装稳定版本的cpp-httplib, 这里推荐 v0.7.15 版本的。 在命令行输入如下命令进行安装。 git clone https://gitee.com/linzhipong/cpp-httplib.git安装网站对应的样例进行测试。
测试代码
#include./cpp-httplib-v0.7.15/httplib.h
int main()
{ httplib::Server svr;svr.Get(/hi, [](const httplib::Request req, httplib::Response res){ res.set_content(Hello World!, text/plain charsetutf-8);//说明charsetutf-8是支持编码的格式即避免中文在网页中显示乱码。}); svr.listen(0.0.0.0, 8080); return 0;
} 在网站上输入http:// ip地址:8080/hi 会显示如下的效果 5.3编写http_server.cc
基本思路
在此之前我们还需对cpp-httplib的接口有一定的了解。
我们需要创建一个http类型的server对象用于接收http请求。假设对象名为svr。对象的Get方法参数需要构建即获取url的路径接收和处理http请求。
请求对象设为 req, 其中有has_param方法判断url中的是否有搜索的word。其中的get_param_value(”word“), 可解析出word的内容。比如url为/s?wordXXX, has_param(“word”)即判断其中是否有word;而get_param_value(“word”)可将XXX提取出来。 提取出请求之后我们可以用之前写的search对象提供对应的搜索服务。 使用响应对象设为其名称为rep其中的set_content方法可帮助我们返回搜索内容上有详细例子可辅助进行理解。 最后别忘了设置服务器的状态为监听状态哦不然服务器可起不来。 实现代码 #include/home/shun_hua/practical-projects/Boost_Search/Modules\
/Server/cpp-httplib/httplib.h#include/home/shun_hua/practical-projects/Boost_Search/Modules\
/Search/search.hpp
#includestring
const std::string root_path /home/shun_hua/practical-projects/\
Boost_Search/Modules/Server/wwwroot;int main()
{bs_search::Searcher search;search.InitSeacher();httplib::Server ser;ser.set_base_dir(root_path.c_str());ser.Get(/s, [](const httplib::Request req, httplib::Response res) {// res.set_content(Hello world!,text/plain);if(!req.has_param(word)){res.set_content(必须要有搜索内容,text/plain; charsetutf-8);return;}std::string word req.get_param_value(word);cout 搜索内容为: word endl;std::string json_str;search.Search(word,json_str);res.set_content(json_str,application/json,charsetutf-8);});ser.listen(0.0.0.0,8080);return 0;
}6. 编写前端模块 说明 HTML : 确定网页的骨骼。CSS: 决定网页的皮肉即是否好看。JavaScirpt: 决定网页的灵魂即动态效果此项目指的是实现前后端交互。 开发环境vscode编写网页较为轻松且可连云服务器比较轻量化。
6.1 HTML
基本知识
meta charsetUTF-8
!-- 字符编码形式为UTF-8的形式--
html langen
!-- 设置网站站点为英文 --
meta nameviewport contentwidthdevice-width, initial-scale1.0
!-- 设置网页的显示形式确保能够正确的显示。--
titleXXXX/title
!-- 设置标题为XXXX--
html..../html用
!-- 中间用来放置 网页文档的内容--
body..../body
!--中间放置网页的可见内容--
div classXXXX/div
!--设置一个类名为XXX的div元素方便进行选择和设置格式。--
h1 aligncenterXXXX/h1
!--设置一个一级标题XXXX并且居中显示。--
input typetext valueXXXXX
!--设置一个输入框内容为XXXX--
button onclick XXXXXXXXX/button
!--设置一个button按钮点击执行XXXX的javascript的函数动作--实现代码
!-- 在vscode下编写只需要按下! 与 Tab键就可生成网页的基本骨架--!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleBoost 搜索引擎/title
/head
bodydiv classcontainer !-- 居中显示--h1 aligncenterBoost/h1div classsearchinput typetext value请输入关键词 button onclickSearch()搜索一下/button/divdiv class search_res!-- 注释部分主要用于CS网页进行测试--!--下面是显示对应的搜索结果,对应搜索结果的对应的具体格式。--!-- div classelemh3a href标题:XXXXXX/a/h3p这是摘要XXXXXXXXXXXXXXXXXXXXXXXXXX/pihttps://developer.mozilla.org/zh-CN/docs/Web/CSS/margin/i/divdiv classelemh3a href标题:XXXXXX/a/h3td alignleftpXXXXXXXXXXXXXXXX/pihttps://developer.mozilla.org/zh-CN/docs/Web/CSS/margin/i/divdiv classelemh3a href标题:XXXXXX/a/h3td alignleftpXXXXXXXXXXXXXXXX/pihttps://developer.mozilla.org/zh-CN/docs/Web/CSS/margin/i/divdiv classelemh3a href标题:XXXXXX/a/h3td alignleftpXXXXXXXXXXXXXXXX/pihttps://developer.mozilla.org/zh-CN/docs/Web/CSS/margin/i/divdiv classelemh3a href标题:XXXXXX/a/h3td alignleftpXXXXXXXXXXXXXXXX/pihttps://developer.mozilla.org/zh-CN/docs/Web/CSS/margin/i/div --/div/div
/body
/html6.2 CSS
基础知识
/*拓展盒子模型流动模型。*//*选择网页的所有内容*/
*{}
/*选择指定的一些标签*/
[标签],[标签]{}
/*选择类名和标签对指的的内容框架进行页表修改。*/
.[类名] [标签]{}/*外边距,单位em,即相对于父元素的外边距的大小。*/
margin: xxem;
/*内边距同外边距。*/
padding: xx em;/*设置高度为 父元素的比例即100%继承父元素*/
height: xxx %;
/*设置高度为 xx 像素点即绝对长度。px是最常用的长度单位。*/
height: xx px;
/*宽度同理*/
width: xxx %;
width: xxx px;/*设置居中对齐*/
margin:0px auto;/*设置盒子与顶部的距离*/
margin-top: xx px;
/*设置盒子与底部的距离*/
margin-bottom: xx px/* 设置边框的颜色和样式参数为2个像素点实体灰边框*/
border: 2px solid grey;
/*设置右边框的样式这里的无。*/
border-right: none;/*设置字体的大小*/
font-size: xx px;
/*设置为字体的样式*/
font-family:xxx;
/*将字体的风格设置为普通字样*/
font-style: normal;
/*设置字体距离内边框的左边距*/
padding-left: xxpx;/*设置字体的颜色, 参数为可选的或者颜色对应的参数。*/
color: xxx;
/*设置背景颜色*/
background-color: xxx;/*设置元素为块级元素独占一行便于调参和修改*/
display: block;
/*将下划线设置为无*/
text-decoration: none;
实现代码:
!-- 网页的CSS部分--
style/*对所有内容去重内外边距 */*{margin:0em; /*外边距*/padding:0; /*内边距*/}/*网页body 和 html的内容完全吻合*/html,body{height: 100%;}/*这是类选择器即选择class container*/.container{/*设置盒子的宽度*/width: 700px;/*通过设置内外边距来达到居中对齐的效果*/margin:0px auto;/*设置盒子与顶部的距离达到美化的效果*/margin-top: 20px;}.container h1{/*设置盒子与顶部的距离达到美化的效果*/width: 600px;margin-bottom: 20px;}/* 复合选择器先选择第一个类再选择第二个类中间用空格隔开。 */.container .search{/*宽度与父标签保持一致*/width: 100%;/*高度设置*/height: 50px;}/*选择form表单中的input标签设置搜索框, 直接选中设置标签的属性*/.container .search input{/*设置left左浮动*/float: left;width: 480px;height: 50px;/* 设置边框的颜色和宽度*/border: 2px solid grey;/* 将右边框设为无*/border-right: none;/* 设置字体的大小*/font-size: 15px;/* 设置字体距离内边框的左边距 */padding-left: 10px;color: #ccc;}.container .search button{/*设置left左浮动*/float: left;width: 120px;height: 54px;border: 2px solid #4e6ef2;border-left: none;/* background-color: blue;*//* 设置按钮颜色 */background-color: #4e6ef2;/* 设置字体颜色 */color: white;/* 设置字体大小 */font-size: 20px;/* font-family:Times New Roman, Times, serif; *//* font-family:Cambria, Cochin, Georgia, Times, Times New Roman, serif; */font-family:cursive;/* font-family:monospace; *//* font-family:serif; */}.container .search_res{margin-top: 30px;}.container .search_res .elem{margin-top: 20px;}.container .search_res .elem a{/*设置为块级元素只占一行*/display: block;/* 将标题的下划线去掉*/text-decoration: none;/* 设置标题的大小 */font-size: 20px;/* 设置标题的颜色 */color: #4e6ef2;}/*设置光标的动作即选中链接显示下划线。*/.container .search_res .elem a:hover{text-decoration: underline;}.container .search_res .elem i{/*设置为块级元素只占一行*/display: block;margin-top: 8px;font-style: normal;font-size: 15px;color: green;}.container .search_res .elem p{margin-top: 8px;font-family: Times New Roman, Times, serif;}/style6.4 JavaScript 说明由于我们不是前端所以为了降低开发难度所以要引入Jquery; script srchttps://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js/script实现思路
提取button中的内容。将内容转换为查询发送给后端。后端返回对应搜索的json串。前端根据返回的json串构建网页内容。
基础知识
/*提取指定标签的内容*/
let query $(xxxxxxx).val()
/*在网页按下F12,显示网页的前端界面点击上面的console运行网页时可看到对应的内容,便于进行测试*/
console.log(query); $.ajax({type: Get,url: /*请求网页内容的资源对应后端的服务于Get请求的界面*/success: function(data){//请求成功时对应的数据//对数据进行处理。}error: function(data){//请求失败时对应的内容。}
});
//选择指定的类里面存放搜索的内容
let res $(xxxxx);
//循环遍历data中的元素,实际在提取时这里的emem为json串可供参数的选择。for(let elem of data)
{/*这里我们设置一个a标签并用元素填充其内容。*/let a_lable $(a,text:elem.title,href:elem.url,target: _blank);//其余的标签内容放在实现中.let div_lable $(div, class: elem);div_lable.appendTo(a_lable)res.appendTo(div_lable)
}
实现代码
function Search()
{// 获取.container .search input中的输入的关键字。let query $(.container .search input).val();$.ajax({type: Get,url: /s?word query,success:function(data){//构建对应的网页BulidHtml(data);}})
}
function BulidHtml(data)
{let result $(.container .search_res); result.empty();for(let elem of data){let a_lable $(a,{text: elem.title,href: elem.url,//点击链接会跳转到新的网页。target: _blank});let p_lable $(p,{text:elem.content});let i_lable $(i,{text:elem.url});let div_lable $(div,{class:elem});a_lable.appendTo(div_lable);p_lable.appendTo(div_lable);i_lable.appendTo(div_lable);div_lable.appendTo(result);}
}总结 与 拓展 总结 此项目我们只是实现了一个基础的版本难度并不是很大并且我们使用了相应的库来降低开发难度。 拓展 暂停词比如is,a,an诸如此类可以进行过滤以便于提高搜索的效率。URL我们可以读取所有的URL这里我们只读取到了/doc/html目录下的html文件。爬虫程序我们这里是通过官方下载也可以通过爬虫程序获取到 html文件。普通的网页可以看到广告的网页我们也可以通过设置系数来完成竞价功能。在搜索界面当我们输入一部分词时会显示相应的可能满足我们搜索要求的即智能显示。设置登录注册引入MySQL。 对于扩展内容博主在这里挖个坑后面有时间和精力会进行补充的。 尾序
我是舜华期待与你的下次相遇 文章转载自: http://www.morning.dcdhj.cn.gov.cn.dcdhj.cn http://www.morning.rhgtc.cn.gov.cn.rhgtc.cn http://www.morning.enjoinfo.cn.gov.cn.enjoinfo.cn http://www.morning.kpfds.cn.gov.cn.kpfds.cn http://www.morning.prgnp.cn.gov.cn.prgnp.cn http://www.morning.dgng.cn.gov.cn.dgng.cn http://www.morning.nptls.cn.gov.cn.nptls.cn http://www.morning.nlkjq.cn.gov.cn.nlkjq.cn http://www.morning.mrbmc.cn.gov.cn.mrbmc.cn http://www.morning.qzmnr.cn.gov.cn.qzmnr.cn http://www.morning.wwsgl.com.gov.cn.wwsgl.com http://www.morning.mqffm.cn.gov.cn.mqffm.cn http://www.morning.lhrcr.cn.gov.cn.lhrcr.cn http://www.morning.rkwwy.cn.gov.cn.rkwwy.cn http://www.morning.yqqxj1.cn.gov.cn.yqqxj1.cn http://www.morning.jytrb.cn.gov.cn.jytrb.cn http://www.morning.mgtmm.cn.gov.cn.mgtmm.cn http://www.morning.msfqt.cn.gov.cn.msfqt.cn http://www.morning.btlsb.cn.gov.cn.btlsb.cn http://www.morning.rrqbm.cn.gov.cn.rrqbm.cn http://www.morning.psyrz.cn.gov.cn.psyrz.cn http://www.morning.qxgmp.cn.gov.cn.qxgmp.cn http://www.morning.mnnxt.cn.gov.cn.mnnxt.cn http://www.morning.yrjfb.cn.gov.cn.yrjfb.cn http://www.morning.tnjff.cn.gov.cn.tnjff.cn http://www.morning.rjbb.cn.gov.cn.rjbb.cn http://www.morning.ghwdm.cn.gov.cn.ghwdm.cn http://www.morning.mxdhy.cn.gov.cn.mxdhy.cn http://www.morning.jzccn.cn.gov.cn.jzccn.cn http://www.morning.nqypf.cn.gov.cn.nqypf.cn http://www.morning.mdmqg.cn.gov.cn.mdmqg.cn http://www.morning.kcfnp.cn.gov.cn.kcfnp.cn http://www.morning.wzyfk.cn.gov.cn.wzyfk.cn http://www.morning.lgsfb.cn.gov.cn.lgsfb.cn http://www.morning.qlsbz.cn.gov.cn.qlsbz.cn http://www.morning.qhqgk.cn.gov.cn.qhqgk.cn http://www.morning.hphfy.cn.gov.cn.hphfy.cn http://www.morning.fhqdb.cn.gov.cn.fhqdb.cn http://www.morning.tkchm.cn.gov.cn.tkchm.cn http://www.morning.rlxg.cn.gov.cn.rlxg.cn http://www.morning.hlxpz.cn.gov.cn.hlxpz.cn http://www.morning.cwtrl.cn.gov.cn.cwtrl.cn http://www.morning.bbrf.cn.gov.cn.bbrf.cn http://www.morning.monstercide.com.gov.cn.monstercide.com http://www.morning.tqsmg.cn.gov.cn.tqsmg.cn http://www.morning.tthmg.cn.gov.cn.tthmg.cn http://www.morning.rhkgz.cn.gov.cn.rhkgz.cn http://www.morning.fjntg.cn.gov.cn.fjntg.cn http://www.morning.yqhdy.cn.gov.cn.yqhdy.cn http://www.morning.mtbsd.cn.gov.cn.mtbsd.cn http://www.morning.krdmn.cn.gov.cn.krdmn.cn http://www.morning.tqxtx.cn.gov.cn.tqxtx.cn http://www.morning.dkqyg.cn.gov.cn.dkqyg.cn http://www.morning.nfgbf.cn.gov.cn.nfgbf.cn http://www.morning.qpqcq.cn.gov.cn.qpqcq.cn http://www.morning.wylpy.cn.gov.cn.wylpy.cn http://www.morning.sfnr.cn.gov.cn.sfnr.cn http://www.morning.hfytgp.cn.gov.cn.hfytgp.cn http://www.morning.dhqyh.cn.gov.cn.dhqyh.cn http://www.morning.xsqbx.cn.gov.cn.xsqbx.cn http://www.morning.wtcbl.cn.gov.cn.wtcbl.cn http://www.morning.rnsjp.cn.gov.cn.rnsjp.cn http://www.morning.sdkaiyu.com.gov.cn.sdkaiyu.com http://www.morning.lxjcr.cn.gov.cn.lxjcr.cn http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn http://www.morning.jwpcj.cn.gov.cn.jwpcj.cn http://www.morning.taojava.cn.gov.cn.taojava.cn http://www.morning.sblgt.cn.gov.cn.sblgt.cn http://www.morning.psgbk.cn.gov.cn.psgbk.cn http://www.morning.qyxwy.cn.gov.cn.qyxwy.cn http://www.morning.bmbnc.cn.gov.cn.bmbnc.cn http://www.morning.lltdf.cn.gov.cn.lltdf.cn http://www.morning.wjzzh.cn.gov.cn.wjzzh.cn http://www.morning.spghj.cn.gov.cn.spghj.cn http://www.morning.jlqn.cn.gov.cn.jlqn.cn http://www.morning.lhrcr.cn.gov.cn.lhrcr.cn http://www.morning.brwp.cn.gov.cn.brwp.cn http://www.morning.mwkwg.cn.gov.cn.mwkwg.cn http://www.morning.fkgct.cn.gov.cn.fkgct.cn http://www.morning.jqsyp.cn.gov.cn.jqsyp.cn