项目日记(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里面.

#include <iostream>
#include <vector>
#include <string>
#include <boost/filesystem.hpp>
#include <utility>
#include <fstream>
#include "util.hpp"
using namespace std;


// 原来没处理过的html数据的文件目录.
const string src_path = "data/input";
// 将数据清洗以及去标签过的数据放到这个目录文件中
const string output = "data/raw_html/raw.txt";

typedef struct DocInfo
{
    string title;
    string content;
    string url;
}DocInfo_t;

//这里规定一下:
//const &: 输入;
//*: 输出;
//&: 输入输出;


bool EnumFile(const string& src_file, vector<string>* file_list);

bool ParserHtml(const vector<string>& file_list, vector<DocInfo>* results);

bool SaveHtml(const vector<DocInfo_t>& result,const string& output);


int main()
{
    //1. 递归将所有的html文件带路径的保存到file_list, 方便对文件进行一个个查询.
    vector<string> file_list;
    if(!EnumFile(src_path, &file_list))
    {
        cerr << "Enum File error!" << endl;
        return 1;
    }

    //2.按照file_list读取每个文件的内容.并且进行解析;
    vector<DocInfo_t> results;
    if(!ParserHtml(file_list, &results))
    {
        cerr << "Parser Html error!" << endl;
        return 2;
    }

    //3.将各个文件进行解析好的文件内容, 写入到output, \3为每个文档的分割符;
    if(!SaveHtml(results, output))
    {
        cerr << "Save Html error!" << endl;
        return 3;
    }

    return 0;
}

EnumFile模块: 

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

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

bool EnumFile(const string& src_path, vector<string>* file_list)
{
    // 这里是标准库<boost/filesystem.hpp>提供的接口和结构体;
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);

    // 实现判断原资源数据路径是否存在.
    if(!fs::exists(root_path))
    {
        cerr << src_path << "no exists" << endl;
        return false;
    }

    //进行html文档的遍历, 如果为空就是遍历完成了.
    fs::recursive_directory_iterator end;
    for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
        //判断是否为普通文件, 因为html都是普通文件.
        if(!fs::is_regular_file(*iter))
        {
            continue;
        }

        //判断是否为html后缀的文件.
        if(iter->path().extension() != ".html")
        {
            continue;
        }
        cout << "debug: " << iter->path().string() << endl;
        //当前路径一定是合法的html为后缀的文件
        file_list->push_back(iter->path().string());
    }

    return true;
}

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删除即可.

static bool ParserTitle(const string& file, string* title)
{
    size_t begin = file.find("<title>");
    if(begin == string::npos)
        return false;
    
    size_t end = file.find("</title");
    if(end == string::npos)
        return false;
    
    begin += string("<title>").size();

    if(begin > end)
        return false;
    
    *title = file.substr(begin, end - begin);
    return true;
}

static bool ParserContent(const string& file, string* content)
{
    //去除html里面的标签;状态机
    enum status
    {
        LABLE,
        CONTENT
    };

    //默认一般html的开头都是标签.
    enum status s = LABLE;
    for(char c : file)
    {
        switch(s)
        {
          case LABLE:
            if(c == '>')
                s = CONTENT;
          case CONTENT:
            if(c == '<')
                s = LABLE;
            else
            {
                //原本文件的\n去除, 因为要用\n来做文本分割符;
                if(c == '\n')
                    c = ' ';
                content->push_back(c);
            }
            break;
          default:
            break;
        }
    }

    return true;
}

static bool ParserUrl(const string& file_path, string* url)
{
    //这个是网址拼接.
    string url_head = "https://www.boost.org/doc/libs/1_85_0/doc/html/accumulators.html";
    string url_tail = file_path.substr(src_path.size());

    *url = url_head + url_tail;
    return true;
}

static void ShowDoc(const DocInfo& doc)
{
    cout << "title: " << doc.title << endl;
    cout << "content: " << doc.content << endl;
    cout << "url: " << doc.url << endl;

}

//将获取的html资源file_list解析处理后放到result;
bool ParserHtml(const vector<string>& file_list, vector<DocInfo_t>* results)
{
    for(const string& file : file_list)
    {
        string result;
        //读取每个文件的内容;
        if(!ns_util::FileUtil::ReadFile(file, &result))
        {
            continue;
        }

        DocInfo_t doc;
        //解析文件, 读取title;
        if(!ParserTitle(result, &doc.title))
        {
            continue;
        }

        //解析文件, 读取content;
        if(!ParserContent(result, &doc.content))
        {
            continue;
        }

        //解析文件, 读取url;
        if(!ParserUrl(result, &doc.url))
        {
            continue;
        }
        
        results->push_back(move(doc)); //提高效率减少拷贝.

        //for test
        ShowDoc(doc);
        break;
    }
    return true;
}

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

#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

namespace ns_util
{
    class FileUtil
    {
    public:
        //读取文件file_path里面的内容并且放到out;
        static bool ReadFile(const string& file_path, string* out)
        {
            ifstream in(file_path, ios::in);
            //如果没有打开文件成功
            if(!in.is_open())
            {
                cerr << "open file" << file_path << "error" << endl;
                return false;
            }

            string line;
            while(!getline(in, line))
            {
                *out += line;
            }
            in.close();
            return true;
        }
    };
};

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

//将解析的数据读写到output每个文档\n分割, 每个文档的title, content, url以\3分割.
bool SaveHtml(const vector<DocInfo_t>& result,const string& output)
{
#define SEP '\3'

    //采用二进制方式写入;
    ofstream out(output, ios::out | ios::binary);
    if(!out.is_open())
    {
        cerr << "open " << output << "failed!" << endl;
        return false;
    }

    for(auto& item : result)
    {
        string out_string;
        out_string = item.title;
        out_string += SEP;
        out_string += item.content;
        out_string += SEP;
        out_string += item.url;
        out_string += '\n';

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

    out.close();
    return true;
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/645166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 Spring 上下文(Context)层次结构

前言 在使用 Spring 框架进行应用程序开发时&#xff0c;Spring 上下文&#xff08;Context&#xff09;是一个非常重要的概念。Spring 上下文提供了一个环境&#xff0c;用于管理应用程序中的对象&#xff08;通常称为 Bean&#xff09;及其之间的依赖关系。在复杂的应用程序…

大模型效能工具之智能CommitMessage

01 背景 随着大型语言模型的迅猛增长&#xff0c;各种模型在各个领域的应用如雨后春笋般迅速涌现。在研发全流程的效能方面&#xff0c;也出现了一系列贯穿全流程的提效和质量工具&#xff0c;比如针对成本较高的Oncall&#xff0c;首先出现了高质量的RAG助手&#xff1b;在开…

【二叉树】:LeetCode:100.相同的数(分治)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 1.问题描述&#xff1a; 2.问题分析&#xff1a; 二叉树是区分结构的&#xff0c;即左右子树是不一…

数据库DCL语句

数据库DCL语句 介绍&#xff1a; DCL英文全称是Data Control Language(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访 问权限。 管理用户&#xff1a; 查询用户: select * from mysql.user;创建用户: create user 用户名主机名 identified by 密码;修改用…

基于开源ATmega8 无感BLDC程序移植到ATmega328PB

基于开源ATmega8 无感BLDC程序移植到ATmega328PB &#x1f516;基于Atmel Studio 7.0开发环境。&#x1f955;开源原项目资源地址&#xff1a;https://svn.mikrokopter.de/websvn/listing.php?repnameBL-Ctrl&path%2F&&#x1f4cd;原理图和PCB资源 BL-Ctrl v2.0 in E…

Keli5烧写STM32程序时出现ST-LINK USB communication error错误(USB 通信错误)

1错误原图 2错误原因 前提驱动安装正确 原因1 usb接触不良&#xff08;极少出现&#xff09; 解决方法 更换USB线 还不行连下载器一起更换 原因2&#xff08;出现概率比较大&#xff09; 下载器的固件出现问题或下载器固件版本与Keli5的版本不匹配 解决方法 在Keli5的…

【python】python tkinter 计算器GUI版本(模仿windows计算器 源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

创建带有公共头部的Electron窗口

创建带有公共头部的Electron窗口 创建一个公共头部的html文件 1.我们在项目根目录创建一个名为app-header的文件夹 2.在app-header创建一个文件名为header.html的文件 结构如下&#xff1a; 基本结构和脚本如下 <body> <div class"header"><div c…

基于STM32+NBIOT(BC26)设计的物联网观赏鱼缸

文章目录 一、前言1.1 项目介绍【1】开发背景【2】项目实现的功能【3】项目模块组成 1.2 设计思路 二、(硬件控制端)硬件选型2.1 STM32开发板2.2 PCB板2.3 USB下载线2.4 NBIOT模块2.5 杜邦线&#xff08;2排&#xff09;2.6 稳压模块2.7 电源插头2.8 水温检测传感器2.9 水质检测…

Python 中别再用 ‘+‘ 拼接字符串了!

当我开始学习 Python 时&#xff0c;使用加号来连接字符串非常直观和容易&#xff0c;就像许多其他编程语言&#xff08;比如Java&#xff09;一样。 然而&#xff0c;很快我意识到许多开发者似乎更喜欢使用.join()方法而不是。 在本文中&#xff0c;我将介绍这两种方法之间的…

计算机网络(1

网络初识 目录 网络初识一. 网络分类1. 局域网LAN(Local Area Network):2. 广域网WAN(Wide Area Network): 二. 组建网络的基础设备1. 路由器2. 交换机 三. 标识符 协议 (protocol)一. 协议分层1. 分层的好处2. OSI七层分层3. TCP/IP五层模型(或四层) 模型(1. 物理层(可不算)(2…

从零开始:手把手教你使用Python实现PDF到Excel的转换

来百 在日常工作和学习中&#xff0c;我们经常会遇到需要将PDF文件中的数据提取到Excel表格中的情况。可能是为了进行数据分析、报告生成或者其他目的。虽然手动复制粘贴是一种方法&#xff0c;但对于大量的数据来说&#xff0c;这种方式显然效率太低。幸运的是&#xff0c;Py…

摸鱼大数据——Hive基础理论知识——Hive基础架构

1、Hive和MapReduce的关系 1- 用户在Hive上编写数据分析的SQL语句&#xff0c;然后再通过Hive将SQL语句翻译成MapReduce程序代码&#xff0c;最后提交到Yarn集群上进行运行 2- 大家可以将Hive理解成有道词典&#xff0c;帮助你翻译英文 2、Hive架构 用户接口: 包括 CLI、JDBC/…

ISCC——AI

得到一个T4.pyc 回编译一下 得到下面代码 import base64def encrypt_and_compare(user_input, offset_str, target_base64):if len(user_input) ! 24:return Please enter a string with a length of 24encrypted Nonefor i, char in enumerate(user_input):offset int(off…

自然资源-各级国土空间总体规划的审查要点及流程总结

自然资源-各级国土空间总体规划的审查要点及流程总结 国土空间规划是对一定区域国土空间开发保护在空间和时间上作出的安排&#xff0c;包括总体规划、详细规划和相关专项规划。 国土空间规划管理是国土空间规划中重要的一环。中共中央、国务院发布《关于建立国土空间规划体系…

C++ Primer Plus第十八章复习题

1、使用用大括号括起的初始化列表语法重写下述代码。重写后的代码不应使用数组ar。 class z200 { private:int j;char ch;double z; public:Z200(int jv,char chv&#xff0c;zv) : j(jv), ch (chv), z(zv){} };double x 8.8; std::string s "what a bracing effect ! …

添砖Java(十一)——常见类的使用Object,Math,System,BigDeciaml,包装类

目录 object&#xff1a; toString&#xff1a; equals: ​编辑 Math&#xff1a;​编辑 System: BigDecimal: 基本数据的包装类&#xff1a;​编辑 object&#xff1a; 我们知道&#xff0c;所有的类都是间接或直接继承了object类。然后object里面有几个用得很多的方法…

使用printf的两种方法,解决printf不能使用的问题

使用printf的两种方法&#xff0c;解决printf不能使用的问题 一、微库法 我们使用printf前要加上重定向fputc //重定义fputc函数 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)0);//循环发送,直到发送完毕 USART1->DR (uint8_t) ch; return…

【JVM实践与应用】

JVM实践与应用 1.类加载器(加载、连接、初始化)1.1 类加载要完成的功能1.2 加载类的方式1.3 类加载器1.4 双亲委派模型1.5自定义ClassLoader1.6 破坏双亲委派模型2.1 类连接主要验证内容2.2 类连接中的解析2.3 类的初始化3.1 类的初始化时机3.2 类的初始化机制和顺序3.2 类的卸…