当前位置:   article > 正文

项目日记(1): boost搜索引擎

项目日记(1): boost搜索引擎

目录

1. 项目相关背景

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

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

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

5. 编写数据去标签化和数据清洗的模块parser(解析器).


1.项目相关背景

百度, 搜狗, 360等都有搜索引擎, 但是都是全网的搜索;

 boost是进行站内搜索, 数据更少并且垂直.

观察通过不同的搜索引擎进行查找的时候会出现不同的关键字, 一般包括title, content, 网址, 时间等.

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

        实现用户通过客户端的浏览器输入关键字, 通过get方式, 然后http发送请求给服务端内存中的searcher, searcher再通过已经抓取好网页并且存放在data/*html目录下进行数据清理去标签建立索引的步骤, 将查找到的多个网页的title, content, url进行拼接, 最后返回给用户一个新的网页.

        在后面实现的部分主要是完成对客户端和服务端的流程, 爬虫网页数据就不做了.

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

技术栈:

        C/C++, C++11, STL, 标准库Boost, Jesoncpp, cppjieba, cpp-httplib, html5, css, js, jQuery, Ajax.

项目环境:

        Centos 7云服务器, vim/g++/Makefile, VSCode.

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

正排索引:  由文档ID找到文档内容;

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

举个栗子:

        妈妈买了四斤蔬菜; 妈妈买了一斤肉; 妈妈买了三两饺子皮.

正排索引: 

倒排索引:

5. 编写数据去标签化和数据清洗的模块parser(解析器)

5.1 官网下载标准库

首先到boost官网去下载好标准库; 网址: https://www.boost.org/

然后使用Xshell将下载好的标准库rz, 然后解包, 将标准库留下.

这里我是创建了一个boost_searcher目录来存放所有需要的文件数据.

其中boost_1_85_0就是boost下载好的标准库.

data目录下的input(从boost_1_85_0里面的doc目录下的html目录的全部数据拿出来的)存放原始html的数据, raw_html是处理过的文件数据.

parser.cc就是用来实现去标签化和数据清洗的函数.

5.2 去标签

一般html文件里面都是出现大量成对的标签, 我们只需要内容, 所以标签对我们没有作用要去除.

去除标签之后的每个文档如何区分捏?

使用\3进行标记区分文档. 读取的时候使用getline(ifstream, line); line就是\3.

5.3 编写parser代码

1. 在boost_searcher里创建一个parser.cc的文件; 

2. 先实现大体的框架; 首先EnumFile递归原html的所有文档, 将它们放到file_list数组里面;

3. ParserHtml从file_list里面将文件读取出来并且解析出来title, content, url;

4. SaveHtml 是将解析好的数据文件写入到output里面.

  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <boost/filesystem.hpp>
  5. #include "util.hpp"
  6. using namespace std;
  7. // 原来没处理过的html数据的文件目录.
  8. const string src_path = "data/input";
  9. // 将数据清洗以及去标签过的数据放到这个目录文件中
  10. const string output = "data/raw_html/raw.txt";
  11. typedef struct DocInfo
  12. {
  13. string title;
  14. string content;
  15. string url;
  16. }DocInfo_t;
  17. // 这里规定一下:
  18. // const &: 输入;
  19. //*: 输出;
  20. //&: 输入输出;
  21. bool EnumFile(const string &src_file, vector<string> *file_list);
  22. bool ParserHtml(const vector<string> &file_list, vector<DocInfo_t> *results);
  23. bool SaveHtml(const vector<DocInfo_t> &results, const string &output);
  24. int main()
  25. {
  26. // 1. 递归将所有的html文件带路径的保存到file_list, 方便对文件进行一个个查询.
  27. vector<string> file_list;
  28. if (!EnumFile(src_path, &file_list))
  29. {
  30. cerr << "Enum File error!" << endl;
  31. return 1;
  32. }
  33. // 2.按照file_list读取每个文件的内容.并且进行解析;
  34. vector<DocInfo_t> results;
  35. if(!ParserHtml(file_list, &results))
  36. {
  37. std::cerr << "parse html error" << std::endl;
  38. return 2;
  39. }
  40. // 3.将各个文件进行解析好的文件内容, 写入到output, \3为每个文档的分割符;
  41. if (!SaveHtml(results, output))
  42. {
  43. cerr << "Save Html error!" << endl;
  44. return 3;
  45. }
  46. return 0;
  47. }

EnumFile模块: 

1.首先使用到标准库里面的接口boost::filesystem以及path来查看是否src_file的资源路径是否存在.

2. 使用ecursive_directory_iterator对src_file资源进行遍历, 再进一步筛选是否为普通文件以及.html后缀的文件. 如果是的话就将其放到数组file_list里面.

  1. bool EnumFile(const string &src_file, vector<string> *file_list)
  2. {
  3. // 这里是标准库<boost/filesystem.hpp>提供的接口和结构体;
  4. namespace fs = boost::filesystem;
  5. fs::path root_path(src_path);
  6. // 实现判断原资源数据路径是否存在.
  7. if (!fs::exists(root_path))
  8. {
  9. cerr << src_path << "no exists" << endl;
  10. return false;
  11. }
  12. // 进行html文档的遍历, 如果为空就是遍历完成了.
  13. fs::recursive_directory_iterator end;
  14. for (fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
  15. {
  16. // 判断是否为普通文件, 因为html都是普通文件.
  17. if (!fs::is_regular_file(*iter))
  18. {
  19. continue;
  20. }
  21. // 判断是否为html后缀的文件.
  22. if (iter->path().extension() != ".html")
  23. {
  24. continue;
  25. }
  26. //cout << "debug: " << iter->path().string() << endl;
  27. // 当前路径一定是合法的html为后缀的文件
  28. file_list->push_back(iter->path().string());
  29. }
  30. return true;
  31. }

1. ParserHtml: 将处理好的源html数据存放在file里的, 对其进行遍历, 然后对文件中的title, content, url进行解析, 最后存放到result数组里面.

2. 然后使用ParserTitle进行将title解析; 因为title的内容都是放在<title> </title>里面; 

我们只要找到<title> 和 </title> 然后将中间的内容切分即可.

3. ParserContent进行去标签获取内容, 采用状态机的方法, 枚举LABLE和CONTENT, 这里需要注意一般html里面开头也是标签, 所以初始化status是LABLE, 对file进行遍历, 如果遇到>那么就代表是标签的结束, status就是CONTENT, 如果遇到<那么就代表是标签的开始, status就是LABLE. 如果遇到\n, 我们需要使用\n做分隔符, 所以要把原来的变成空串, 最后将拼接好的字符串放到content.

4. ParserUrl进行url的解析, 那么我们boost官网查询和我们存放在自己的路径是不同的.

上面是url_head, 下面的url_tail只shangchu需要将data/input删除即可.

  1. static bool ParseTitle(const std::string &file, std::string *title)
  2. {
  3. std::size_t begin = file.find("<title>");
  4. if(begin == std::string::npos)
  5. {
  6. return false;
  7. }
  8. std::size_t end = file.find("</title>");
  9. if(end == std::string::npos)
  10. {
  11. return false;
  12. }
  13. begin += std::string("<title>").size();
  14. if(begin > end)
  15. {
  16. return false;
  17. }
  18. *title = file.substr(begin, end - begin);
  19. return true;
  20. }
  21. static bool ParseContent(const std::string &file, std::string *content)
  22. {
  23. //去标签,基于一个简易的状态机
  24. enum status
  25. {
  26. LABLE,
  27. CONTENT
  28. };
  29. enum status s = LABLE;
  30. for( char c : file)
  31. {
  32. switch(s)
  33. {
  34. case LABLE:
  35. if(c == '>') s = CONTENT;
  36. break;
  37. case CONTENT:
  38. if(c == '<') s = LABLE;
  39. else
  40. {
  41. //我们不想保留原始文件中的\n,因为我们想用\n作为html解析之后文本的分隔符
  42. if(c == '\n') c = ' ';
  43. content->push_back(c);
  44. }
  45. break;
  46. default:
  47. break;
  48. }
  49. }
  50. return true;
  51. }
  52. static bool ParseUrl(const std::string &file_path, std::string *url)
  53. {
  54. std::string url_head = "https://www.boost.org/doc/libs/1_85_0/doc/html";
  55. std::string url_tail = file_path.substr(src_path.size());
  56. *url = url_head + url_tail;
  57. return true;
  58. }
  59. static void ShowDoc(const DocInfo_t &doc)
  60. {
  61. cout << "title: " << doc.title << endl;
  62. cout << "content: " << doc.content << endl;
  63. cout << "url: " << doc.url << endl;
  64. }
  65. // 将获取的html资源file_list解析处理后放到result;
  66. bool ParserHtml(const vector<string> &file_list, vector<DocInfo_t> *results)
  67. {
  68. for(const std::string &file : file_list)
  69. {
  70. //1. 读取文件,Read();
  71. std::string result;
  72. if(!ns_util::FileUtil::ReadFile(file, &result))
  73. {
  74. continue;
  75. }
  76. DocInfo_t doc;
  77. //2. 解析指定的文件,提取title
  78. if(!ParseTitle(result, &doc.title))
  79. {
  80. continue;
  81. }
  82. //3. 解析指定的文件,提取content,就是去标签
  83. if(!ParseContent(result, &doc.content))
  84. {
  85. continue;
  86. }
  87. //4. 解析指定的文件路径,构建url
  88. if(!ParseUrl(file, &doc.url))
  89. {
  90. continue;
  91. }
  92. //ShowDoc(doc);
  93. //done,一定是完成了解析任务,当前文档的相关结果都保存在了doc里面
  94. results->push_back(std::move(doc)); //bug:todo;细节,本质会发生拷贝,效率可能会比较低, move也可以防止拷贝所以后面的ShowDoc打印不出来.
  95. }
  96. return true;
  97. }

util.hpp: FileUtil是将每条源html的资源进行输出;

  1. namespace ns_util
  2. {
  3. class FileUtil
  4. {
  5. public:
  6. static bool ReadFile(const std::string &file_path, std::string *out)
  7. {
  8. std::ifstream in(file_path, std::ios::in);
  9. if(!in.is_open())
  10. {
  11. std::cerr << "open file " << file_path << " error" << std::endl;
  12. return false;
  13. }
  14. std::string line;
  15. while(std::getline(in, line))
  16. {
  17. *out += line;
  18. }
  19. in.close();
  20. return true;
  21. }
  22. };
  23. };

SaveHtml:将解析好的数据进行文档分割和内容分割. 将解析好的数据以这种方式写入到output.

  1. // 将解析的数据读写到output每个文档\n分割, 每个文档的title, content, url以\3分割.
  2. bool SaveHtml(const vector<DocInfo_t> &result, const string &output)
  3. {
  4. #define SEP '\3'
  5. // 采用二进制方式写入;
  6. ofstream out(output, ios::out | ios::binary);
  7. if (!out.is_open())
  8. {
  9. cerr << "open " << output << "failed!" << endl;
  10. return false;
  11. }
  12. for (auto &item : result)
  13. {
  14. string out_string;
  15. out_string = item.title;
  16. out_string += SEP;
  17. out_string += item.content;
  18. out_string += SEP;
  19. out_string += item.url;
  20. out_string += '\n';
  21. out.write(out_string.c_str(), out_string.size());
  22. }
  23. out.close();
  24. return true;
  25. }

后言: 好久没更新博客了, xdm求个三联, 多多更新超多计算机网络, Linux, c++, c, 算法内容.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/712686
推荐阅读
相关标签
  

闽ICP备14008679号