国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【項目】Boost搜索引擎

這篇具有很好參考價值的文章主要介紹了【項目】Boost搜索引擎。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

Boost搜索引擎

1. 項目的相關(guān)背景

研發(fā)搜索引擎的公司,如百度、搜狗、360搜索,還有各大網(wǎng)站各種客戶端也提供搜索功能

為什么選擇實(shí)現(xiàn)Boost搜索引擎

1)因為Boost官方網(wǎng)站是沒有搜索功能的,所以我們可以為Boost實(shí)現(xiàn)一個站內(nèi)搜索引擎,雖然官方提供了boost相關(guān)的一些方法,標(biāo)準(zhǔn)庫中的一些接口,但是我們想看到官方文檔成本比較高,所以我們可以自己做一個站內(nèi)搜索

2)自行實(shí)現(xiàn)一個全網(wǎng)搜索引擎難度極大,是十分困難的,但是實(shí)現(xiàn)站內(nèi)搜索,也就是只搜索網(wǎng)站內(nèi)的內(nèi)容,這樣搜索的內(nèi)容更垂直(即:搜索的內(nèi)容有很強(qiáng)的相關(guān)性 ),數(shù)據(jù)量更小,也可以達(dá)到以小見大的效果,


對于搜索結(jié)果,基本包含三個部分:網(wǎng)頁標(biāo)題,網(wǎng)頁內(nèi)容摘要,目標(biāo)網(wǎng)頁地址

【項目】Boost搜索引擎

我們還可以發(fā)現(xiàn),有時候我們可以搜到一個廣告推銷,本質(zhì)上,廣告是搜索引擎的一種盈利的方式,每一個客戶在用搜索引擎的時候,都要搜索關(guān)鍵字,所以這些搜索引擎可以出售這些關(guān)鍵字, 誰給的錢越多,誰的搜索結(jié)果就越靠前


2.搜索引擎的相關(guān)宏觀原理

【項目】Boost搜索引擎

程序跑起來,一定是在內(nèi)存當(dāng)中跑

1)還沒有進(jìn)行搜索之前,首先需要在全網(wǎng)當(dāng)中抓取網(wǎng)頁,假設(shè)放在data目錄下,里面包含了抓取的所有的網(wǎng)頁信息

2)把網(wǎng)頁抓取下來之后,對內(nèi)容進(jìn)行去標(biāo)簽,數(shù)據(jù)清理, 只保留網(wǎng)頁的內(nèi)容,標(biāo)題,網(wǎng)頁的url

3)searcher建立索引,既然要搜索肯定要建立索引:這個索引作用是為了加速查找

4)開始搜索,服務(wù)器一旦啟動,瀏覽器就需要通過http請求的方式進(jìn)行搜索任務(wù), (本質(zhì)還是提交http請求,然后在我們的服務(wù)端執(zhí)行搜索任務(wù))


3. 相關(guān)技術(shù)棧和項目環(huán)境

技術(shù)棧

  • 后端:C/C++,C++11,STL,Boost標(biāo)準(zhǔn)庫,Jsoncpp,cppjieba,cpp-httplib
    • jsoncpp:對響應(yīng)的內(nèi)容完成序列化的操作
    • cppjieba:對搜索關(guān)鍵字分詞,組合成各種搜索的關(guān)鍵字然后進(jìn)行文檔搜索
    • cpp-httplib:開源庫,構(gòu)建http服務(wù)器
  • 前端:html5,jQuery,Ajax

項目環(huán)境

Centos7云服務(wù)器,vim/gcc(g++)/Makefile,vscode


4.搜索引擎具體原理-正排索引 && 倒排索引

搜索引擎必然要對內(nèi)容建立索引,才能更快的搜索和返回,有兩種索引:正排索引和倒排索引,以如下內(nèi)容舉例:

有如下兩個文檔,我們對這兩個文檔內(nèi)容建立索引:

文檔1:雷軍買了四斤小米 文檔2:雷軍發(fā)布了小米手機(jī)

正排索引

建立正排索引本質(zhì)就是建立文檔ID和文檔內(nèi)容的對應(yīng)關(guān)系,正排索引就是根據(jù)文檔ID找到文檔內(nèi)容

文檔ID 文檔內(nèi)容
1 雷軍買了四斤小米
2 雷軍發(fā)布了小米手機(jī)

對文檔分詞

拿到文檔首先要對其編號,其次對文檔內(nèi)容進(jìn)行分詞,也就是得到文檔內(nèi)的關(guān)鍵字,為的是建立倒排索引和方便查找

  • 對于文檔1:雷軍買了四斤小米 可以分詞為:雷軍/買/了/四斤/小米/四斤小米
  • 對于文檔2:雷軍發(fā)布了小米手機(jī) 可以分詞為:雷軍/發(fā)布/了/小米/小米手機(jī)

其中對于“了”、“呢”、“的”、“啊”,這些詞都被稱為停止詞或暫停詞,這些詞對我們建立索引是沒有意義的,一般我們在分詞的時候可以不考慮,因為這些詞的出現(xiàn)頻率太高了,如果保留下來,搜索的時候區(qū)分唯一性的價值不大,會增加建立索引的成本,乃至于增加搜索的成本

倒排索引

倒排索引就是根據(jù)文檔內(nèi)容,進(jìn)行分詞,整理具有唯一性不重復(fù)的關(guān)鍵字,再根據(jù)關(guān)鍵字找到關(guān)聯(lián)文檔ID的方案,簡單來說就是:根據(jù)關(guān)鍵字,找到其在哪些文檔ID出現(xiàn)過

關(guān)鍵字(具有唯一性) 文檔ID(在哪些文檔出現(xiàn)過)
雷軍 1,2
1
四斤 1
小米 1,2
四斤小米 1
發(fā)布 2
小米手機(jī) 2

模擬一次搜索的過程:

用戶輸入:小米 -> 在倒排索引中查找該關(guān)鍵字,提取出文檔ID (1, 2) -> 根據(jù)文檔ID查出正排索引,找到文檔內(nèi)容 ->獲取文檔的標(biāo)題、內(nèi)容、描述、URL -> 對文檔結(jié)果進(jìn)行摘要 -> 構(gòu)建響應(yīng)并返回

  • 可能一個關(guān)鍵字出現(xiàn)在多個文檔當(dāng)中,所以我們可以根據(jù)誰的權(quán)重更高,就把誰的文檔放在前面展示

5.數(shù)據(jù)去標(biāo)簽與數(shù)據(jù)清洗的模塊

獲取數(shù)據(jù)源:

boost官網(wǎng): https://www.boost.org/

1.下載搜索的數(shù)據(jù)源: https://www.boost.org/users/history/version_1_78_0.html

【項目】Boost搜索引擎

2.通過拖拽/re -E指令,把壓縮包上傳到云服務(wù)器當(dāng)中

3.然后進(jìn)行解包: tar xzf 壓縮包名字

【項目】Boost搜索引擎

實(shí)際上我們查的大部分的手冊,都是在doc目錄下的html

【項目】Boost搜索引擎


【項目】Boost搜索引擎

這個就是標(biāo)準(zhǔn)庫對應(yīng)的各種boost組件對應(yīng)的手冊內(nèi)容,就是一個個的網(wǎng)頁信息 ,就是我們的數(shù)據(jù)源


創(chuàng)建一個名字為data的目錄,里面包含一個input目錄,input里面放我們的數(shù)據(jù)源, 把上述的數(shù)據(jù)源拷貝到input里面

【項目】Boost搜索引擎

目前我們只需要boost_1_78_0/doc/html目錄下的html文件,用它來進(jìn)行建立索引


認(rèn)識標(biāo)簽和去標(biāo)簽

現(xiàn)在我們首先將數(shù)據(jù)源中的各個文檔去標(biāo)簽化,HTML 是標(biāo)簽化語言,所有的語句都被一對標(biāo)簽包裹起來,由左右尖括號括起來的就是標(biāo)簽,對數(shù)據(jù)本身是無意義的,所以我們首先要將其去掉

  • 一般標(biāo)簽都是成對出現(xiàn)的,標(biāo)簽中的屬性信息也是不需要的,只有標(biāo)簽內(nèi)的數(shù)據(jù)是有用數(shù)據(jù)
<!--例子-->
<title>Chapter 37. Boost.STLInterfaces</title> 
<link rel="stylesheet" href="../../doc/src/boostbook.css" type="text/css">
<td align="center"><a href="../../index.html">Home</a></td>

我們可以創(chuàng)建一個目錄存放清洗干凈的html文檔

【項目】Boost搜索引擎

其中:input當(dāng)中放的是:原始的html文檔,也就是數(shù)據(jù)源 raw_html/raw.txt當(dāng)中放的是:去標(biāo)簽之后的干凈文檔內(nèi)容


我們可以看一下當(dāng)前有多少個html數(shù)據(jù)源:

【項目】Boost搜索引擎

我們當(dāng)前的目標(biāo)就是:把每個文檔都進(jìn)行去標(biāo)簽,把清洗后的內(nèi)容寫到同一個文件中, 寫入到文件當(dāng)中,一定要考慮下一次在讀取的時候也要方便操作

  • 我們選擇的方案是: html文件內(nèi)部:以\3分割標(biāo)題,內(nèi)容,鏈接 html文檔和html文檔之間以\n區(qū)分
類似:title\3content\3url \n title\3content\3url \n title\3content\3url \n ...

這樣我們使用getline(ifsream, line),一次讀取一行內(nèi)容,相當(dāng)于直接獲取一個html文檔經(jīng)過清洗之后的全部內(nèi)容:title\3content\3url

注意:這里只需要使用兩個\3即可,不需要使用三個\3:title\3content\3url\3 \n,因為兩個分隔符就可以分隔三個東西


問題:為什么使用\3作為分隔符

首先我們要知道,控制字符是不可以顯示的,即下述綠色框框中的(其ascii碼值在0~31,127)的字符,而有一些字符是打印字符,現(xiàn)在的我們的文檔內(nèi)容是屬于打印字符的范疇

我們使用\3作為分隔符,因為\3是控制字符是控制字符,不顯示,不會污染我們的文檔內(nèi)容,當(dāng)然,我們也可以使用\4之類的不可顯字符作為分割字符

【項目】Boost搜索引擎

注意: \3,在文檔當(dāng)中是以^c 即出現(xiàn)


關(guān)于參數(shù)的說明

const &	表示是輸入型參數(shù)
&		表示是輸入輸出型參數(shù)
* 		表示是輸出型參數(shù)

編寫parser.cc

基本框架

主要過程:

1.獲取所有的帶路徑的文件名數(shù)據(jù)源html,進(jìn)行解析,解析成:title,content,url的形式

2.把解析后的數(shù)據(jù)放到清洗后數(shù)據(jù)存放位置

//首先我們涉及到讀取文件的動作,我們先把將要讀取的文件的路徑定義出來,方便我們進(jìn)行讀取
const std::string src_path = "data/input"; // html網(wǎng)頁數(shù)據(jù)源路徑,input下面放的是所有的html網(wǎng)頁
const std::string output = "data/raw_html/raw.txt"; // 文檔數(shù)據(jù)清洗之后的保存路徑

// 解析html文檔,每一個html文檔都被拆成下面的樣子:
typedef struct DocInfo_t 
{
   
    std::string title;       // 文檔標(biāo)題
    std::string content;     // 文檔內(nèi)容
    std::string url;         // 該文檔在官網(wǎng)中的url
}DocInfo_t;

//先聲明
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文件
    //第一步:遞歸式的把src_path路徑下的每個html文件名帶路徑,保存到files_list中
    //為什么這樣做? 方便后期進(jìn)行一個一個的文件進(jìn)行讀取
    if(!EnumFile(src_path, &files_list)) //枚舉所有帶路徑的文件名
    {
   
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }    

    //第二步:文件內(nèi)容的解析
    std::vector<DocInfo_t> results;//存放每個文檔解析完的內(nèi)容-DocInfo_t結(jié)構(gòu)
    if(!ParseHtml(files_list, &results)){
    //本質(zhì)是讀取files_list路徑的文件內(nèi)容并且解析
        std::cerr << "parse html error" << std::endl;
        return 2;
    }
    //第三步: 把解析完畢的各個文件內(nèi)容寫入到output路徑對應(yīng)的文件
    if(!SaveHtml(results, output)){
   
        std::cerr << "sava html error" << std::endl;
        return 3;
    }
    return 0;
}

枚舉帶路徑的html文件

C++對文件系統(tǒng)的支持并不是很好,所以我們需要使用的是boost庫的file system模塊

#include <boost/filesystem.hpp> //引入boost庫的file system模塊

首先,我們需要在centos下安裝在boost庫 sudo yum install -y boost-devel

  • 這里需要注意的是:

【項目】Boost搜索引擎

我們進(jìn)行搜索的是1.78版本的官方手冊, 而我們用的boost庫的版本是1.53 來寫代碼,這個是兩碼事


不建議直接將命名空間展開,防止命名沖突 ,減少沖突概率

第一步: 使用boost::filesystem命名空間下的path類型定義一個路徑對象,并使用我們的參數(shù)路徑進(jìn)行初始化,需要判斷當(dāng)前路徑釋放是存在的

第二步:定義一個空迭代器,然后遍歷該路徑的文件,如果該文件不是普通文件就不處理,如果是普通文件還要判斷后綴是不是以html結(jié)尾,

//第一個參數(shù):所有文件保存的路徑	第二個參數(shù):輸出型參數(shù),枚舉的文件名保存的位置
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
   
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);//定義一個path類型的對象,遍歷的時候就從這個路徑下開始

    //判斷root_path這個路徑是否存在,如果不存在,就沒有必要再往后走了
    if(!fs::exists(root_path))
    {
   
        //注意:這里不能直接打印root_path,因為它是用boost庫的filesystem定義的對象,是一個對象??!
        std::cerr << src_path << " not exists" << std::endl;
        return false;
    }

    //對文件遞歸遍歷,定義一個空的迭代器,用來進(jìn)行判斷遞歸結(jié)束
    fs::recursive_directory_iterator end;//迭代器是模擬指針的行為,這個空迭代器可以認(rèn)為是nullptr
    //從root_path開始遍歷  iter也是迭代器對象,用root_path構(gòu)造它
    for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
   
        //對文件進(jìn)行篩選,判斷文件是否是普通文件:html都是普通文件
        if(!fs::is_regular_file(*iter))
        {
    
            continue;//不是普通文件就不處理
        }
        //這個普通文件還必須以html結(jié)尾,判斷文件后綴
        //iter->path():返回當(dāng)前迭代器所在的路徑   extension方法:提取路徑的后綴
        if(iter->path().extension() != ".html"){
    //判斷 文件路徑名的后綴是否符合要求
            continue; 
        }
        //測試: 來到這里,說明當(dāng)前的路徑一定是一個合法的,以.html結(jié)束的普通網(wǎng)頁文件
        //std::cout << "debug: " << iter->path().string() << std::endl;
        
        //iter->path()得到的還是路徑對象, string():將對象所對應(yīng)的路徑以字符串的形式呈現(xiàn)出來
        files_list->push_back(iter->path().string()); //將所有帶路徑的html保存在files_list,方便后續(xù)進(jìn)行文本分析
    }
    return true;
}

解析html文件

大致框架

先讀取文件的所有內(nèi)容,再依次解析文件的 title、content、url,解析成功后拷貝至解析結(jié)果數(shù)組中

//第一個參數(shù):保存所有帶路徑的html文件	第二個參數(shù):輸出型參數(shù),解析結(jié)果數(shù)組
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
   
    //遍歷所有html文件路徑
    for(const std::string &file : files_list)
    {
   
        //1. 讀取文件,file是帶路徑的html文件,打開它然后把它的內(nèi)容全部讀取出來
        std::string result; //把讀取到的內(nèi)容放到result當(dāng)中
        if(!ns_util::FileUtil::ReadFile(file, &result)){
   
            continue;//讀取失敗,說明文件打開失敗,不管它
        }
        //此時result當(dāng)中保存的就是網(wǎng)頁的內(nèi)容
        DocInfo_t doc;
        //2. 解析指定的文件,提取title
        if(!ParseTitle(result, &doc.title)){
    
            continue;
        }
        //3. 解析指定的文件,提取content,本質(zhì)就是去標(biāo)簽,只保留網(wǎng)頁的內(nèi)容
        if(!ParseContent(result, &doc.content)){
       
            continue;
        }   
        //4. 解析指定的文件路徑,構(gòu)建url
        if(!ParseUrl(file, &doc.url)){
   
            continue;
        }
        //來到這里,一定是完成了解析任務(wù),當(dāng)前文檔的相關(guān)內(nèi)容:title,content,url都保存在了doc結(jié)構(gòu)體里面
        //results->push_back(doc) //細(xì)節(jié):push_back,本質(zhì)會發(fā)生拷貝,效率可能會比較低,所以直接移動
        results->push_back(std::move(doc)); //doc是臨時對象,相當(dāng)于是資源轉(zhuǎn)移
    }
    return true;
}

注意:上述提取title和content, 傳入的第一個參數(shù)是當(dāng)前獲得的html文件的內(nèi)容,而構(gòu)建ur傳入的第一個參數(shù)則是當(dāng)前html文件的路徑,下面詳解!


讀取html文件

我們可以準(zhǔn)備一個Util.hpp文件,存放我們的工具類,并且把內(nèi)容都放在ns_util命名空間下面,所以我們可以單獨(dú)把讀取文件的函數(shù)寫在一個類里面:

我們可以直接使用ifstream流進(jìn)行讀取,使用getline一次讀取一行內(nèi)容,放在out里面,最后記得關(guān)閉文件流?。?!

class FileUtil
{
   
public:
    //第一個參數(shù):該文件的路徑 第二個參數(shù):輸出型參數(shù),讀到的文件內(nèi)容返回出去
    static bool ReadFile(const std::string &file_path, std::string *out)
    {
   
        std::ifstream in(file_path, std::ios::in); //in表示讀取
        if(!in.is_open()) //判斷是否打開成功
        {
   
            std::cerr << "open file " << file_path << " error" << std::endl;
            return false;
        }
        //文件讀取
        std::string line; //line保存的就是讀取到的一行內(nèi)容
        while(std::getline(in, line))
        {
    
            *out += line;
        }

        in.close(); //關(guān)閉文件
        return true;
    }
};

問題:如何理解getline讀取到文件結(jié)束呢?

getline的返回值是一個流的引用,while循環(huán)判斷是一個bool類型 ,本質(zhì)是因為返回的對象當(dāng)中重載了強(qiáng)制類型轉(zhuǎn)化,意思就是判斷對象的真假,這個對象里面就做了重載,重載強(qiáng)轉(zhuǎn)之后得到的就是bool值


下面三個:提取title,content,構(gòu)建url的三個函數(shù)都設(shè)置為static靜態(tài)函數(shù),我們只想在本源文件中有效

提取title

一般在html網(wǎng)頁里面只有一對title,我們要做的就說提取這對標(biāo)簽里面的內(nèi)容

【項目】Boost搜索引擎

其實(shí)很簡單,只需要找到<title></title>位置即可, begin指向第一個<位置,end指向第二個<位置,然后begin跳過<title>個長度,來到標(biāo)題的正文位置, 此時[begin,end)就是標(biāo)題的內(nèi)容

【項目】Boost搜索引擎

//第一個參數(shù):file里面就是網(wǎng)頁內(nèi)容	第二個參數(shù):輸出型參數(shù),存放當(dāng)前網(wǎng)頁的標(biāo)簽內(nèi)容
static bool ParseTitle(const std::string &file, std::string *title)//解析網(wǎng)頁的標(biāo)題內(nèi)容
{
   
    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移動到標(biāo)題內(nèi)容的起始位置
    begin += std::string("<title>").size();
    if(begin > end){
    
        return false;
    }
    *title = file.substr(begin, end - begin);//[begin,end)
    return true;
}

提取content

本質(zhì)是進(jìn)行去標(biāo)簽,在遍歷整個html文件內(nèi)容的時候,只要碰到>就意味著當(dāng)前標(biāo)簽被處理完畢,只要碰到<就意味著即將處理新標(biāo)簽, 所以可以用枚舉類型描述這2種狀態(tài),條件就緒更改狀態(tài),遇到內(nèi)容時就插入到對應(yīng)字符串中

//去標(biāo)簽(數(shù)據(jù)清洗)操作
static bool ParseContent(const std::string &file, std::string *content)
{
    
    //基于一個簡易的狀態(tài)機(jī)
    enum status{
     //枚舉兩種狀態(tài)
        LABLE,//正在讀的是標(biāo)簽里面的內(nèi)容
        CONTENT//現(xiàn)在讀取的是正常文本內(nèi)容
    };

    enum status s = LABLE;//剛開始一定是標(biāo)簽 
    //file:就是整個html文件的內(nèi)容(網(wǎng)頁的內(nèi)容), 按字符讀取 
    for( char c : file)//這里沒有加引用!??!
    {
    
        switch(s)    
        {
   
            //此時是標(biāo)簽狀態(tài),說明該字符可能是標(biāo)簽的一部分,如果是,則忽略,繼續(xù)下次循環(huán)讀取
            //但是需要注意:如果當(dāng)前字符是右標(biāo)簽,說明下一次就是正常的文本內(nèi)容
            case LABLE: 
                if(c == '>') s = CONTENT; //碰到右標(biāo)簽 ->標(biāo)簽狀態(tài)結(jié)束
                break;
            //當(dāng)前是文本狀態(tài),說明該字符可能是文本的一部分,如果是則插入到輸出型參數(shù)當(dāng)中
            //但是需要注意:如果當(dāng)前字符是左標(biāo)簽,說明下一次就是讀取標(biāo)簽的內(nèi)容
            case CONTENT:
                if(c == '<') 
                    s = LABLE;//碰到左標(biāo)簽 ->內(nèi)容讀取完了,變換狀態(tài)
                else //意味著此時是內(nèi)容的狀態(tài),并且不是左標(biāo)簽
                {
   
                    //小細(xì)節(jié):我們不想保留原始文件中的\n,因為我們想用\n作為html解析之后文本的分隔符
                    //所以我們把\n換成空格!所以這也是為什么不加引用的原因!否則就對源html文件做修改了
                    if(c == '\n') c = ' ';
                    content->push_back(c);//插入當(dāng)前字符
                }
                break;
            default:
                break;
        }
    }
    return true;
}
構(gòu)建url

需要注意:boost庫的官方文檔,和我們下載下來的文檔的路徑,是有對應(yīng)關(guān)系的

【項目】Boost搜索引擎

官網(wǎng)URL樣例:    https://www.boost.org/doc/libs/1_78_0/doc/html/accumulators.html
我們下載下來的url樣例:  boost_1_78_0/doc/html/accumulators.html
我們拷貝到我們項目中的樣例: data/input/accumulators.html

所以:我們只需要構(gòu)造url的頭部為:url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html"

然后url的尾部為: url_tail = /accumulators.html即可,也就是把我們項目當(dāng)中的樣例前面的 data/input刪掉

url = url_head + url_tail ; 相當(dāng)于形成了一個官網(wǎng)鏈接


//第一個參數(shù):文件在當(dāng)前項目的路徑	第二個參數(shù):輸出型參數(shù)
static bool ParseUrl(const std::string &file_path, std::string *url) //構(gòu)建官網(wǎng)鏈接
{
   
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";//前綴
    //最初我們最開始已經(jīng)定義了路徑src_path = "data/input" 放的是所有的html數(shù)據(jù)源
    //file_path就相當(dāng)于:data/input/accumulators.html 然后把前面的內(nèi)容data/input去掉
    std::string url_tail = file_path.substr(src_path.size());
	//構(gòu)成官網(wǎng)鏈接
    *url = url_head + url_tail;
    return true;
}

寫法2: 不使用src_path

bool ParserUrl(const std::string& file, std::string* url)
{
   
    std::string url_head = "https://www.boost.org/doc/libs/1_80_0/doc/html/"; // 構(gòu)建前綴,注意這里html后面加了個/
    int begin = file.rfind('/');//注意是從后往前找/ 
    if (begin == std::string::npos) {
   
        std::cout << "file suffix find error" << std::endl;
        return false;
    }
	//[begin+1,file的結(jié)束位置]就是后綴
    std::string url_tail(file, begin + 1);
    *url = url_head + url_tail;
    return true;
}

數(shù)據(jù)保存

把解析完畢的各個文件內(nèi)容寫入到output路徑對應(yīng)的文件, 以\3為文檔內(nèi)容之間的區(qū)分, 文檔與文檔之間用\n區(qū)分

title\3content\3url \n title\3content\3url \n title\3content\3url \n 

這樣我們使用getline一次讀取一行能夠直接獲得一個html文檔的屬性信息

//第一個參數(shù):經(jīng)過清洗之后的html文件屬性信息數(shù)組	第二個參數(shù):保存解析完畢的文件內(nèi)容
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)
{
   
    #define SEP '\3'  //分隔符
    //這里按照二進(jìn)制方式進(jìn)行寫入,但是用文本寫入也是可以的,但是這里因為有\(zhòng)3
    //二進(jìn)制寫的特點(diǎn)是:寫入的是什么,文檔里保存的就是什么,程序不會給我們做自動轉(zhuǎn)化
    std::ofstream out(output, std::ios::out | std::ios::binary);//因為是要輸出->所以是out,以二進(jìn)制形式寫
    if(!out.is_open()){
   //打開文件失敗
        std::cerr << "open " << output << " failed!" << std::endl;
        return false;
    }
    //遍歷數(shù)組
    for(auto &item : results) //item就是DocInfo_t結(jié)構(gòu),放的是一個html網(wǎng)頁的標(biāo)題,內(nèi)容,鏈接
    {
   
        //文檔內(nèi)的分隔符是\3    文檔間的分割方是\n
        std::string out_string;
        out_string = item.title;//標(biāo)題
        out_string += SEP;// \3
        out_string += item.content;//內(nèi)容
        out_string += SEP;// \3
        out_string += item.url; //鏈接
        out_string += '\n'; //一個文檔和一個文檔的分割符是\n

        //第一個參數(shù):要寫入的數(shù)據(jù), 第二個參數(shù):寫入的字節(jié)數(shù)
        out.write(out_string.c_str(), out_string.size());//把字符串的內(nèi)容寫到文件

       /*
      std::string out_string(item._title + SEP + item._content + SEP + item._url + '\n');
      out.write(out_string.c_str(), out_string.size()); 
      */
    }
    out.close();//記得關(guān)閉流!
    return true;
}


用于debug函數(shù)

//for debug
static void ShowDoc( const DocInfo_t &doc)
{
   
    std::cout << "title: " << doc.title << std::endl;
    std::cout << "content: " << doc.content << std::endl;
    std::cout << "url: " << doc.url << std::endl;
}

可以在解析完html文件之后,調(diào)用該函數(shù)調(diào)試!由于內(nèi)容過多,所以可以選擇輸出一個之后就break退出循環(huán)


運(yùn)行結(jié)果:

【項目】Boost搜索引擎

我們之前看到的是,data/input目錄下一共有8141個html文件, 現(xiàn)在處理之后一共有8141行,符合我們的預(yù)期!raw.txt文件內(nèi)部的每一行都是一個網(wǎng)頁對應(yīng)的內(nèi)容:標(biāo)題\3內(nèi)容\3網(wǎng)頁鏈接 \n

【項目】Boost搜索引擎

\3對應(yīng)的值就是^c ,屬性之間的分隔符是\3顯示為^C,文檔之間的分隔符是\n不顯示,并且我們可以賦值網(wǎng)址到瀏覽器搜索,看是否正確

可以直接 cat raw.txt | head -1 //查看前面的幾行/后面的幾行進(jìn)行驗證


如果url出現(xiàn)問題,調(diào)試,輸出結(jié)果看是否符合預(yù)期,

例如:可能是src_path的路徑為:data/input/導(dǎo)致的錯誤,因為我們的url_head最后面的html是沒有加/的 此時我們對data/input/accumulators.html截取尾部獲得的就是accumulators.html,所以拼接起來就是:https://www.boost.org/doc/libs/1_78_0/doc/htmlaccumulators.html

解決辦法: src_path后面的路徑不加/ ,直接就是data/input ,或者url_head的html后面加個/,也就是https://www.boost.org/doc/libs/1_78_0/doc/html/

【項目】Boost搜索引擎文章來源地址http://www.zghlxwxcb.cn/news/detail-438571.html


匯總:parser.cc

#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp> //引入boost庫
#include "util.hpp"

//首先我們涉及到讀取文件的動作,我們先把將要讀取的文件的路徑定義出來,方便我們進(jìn)行讀取
const std::string src_path = "data/input"; // html網(wǎng)頁數(shù)據(jù)源路徑,input下面放的是所有的html網(wǎng)頁
const std::string output = "data/raw_html/raw.txt"; // 文檔數(shù)據(jù)清洗之后的保存路徑

// 解析html文檔,每一個html文檔都被拆成下面的樣子:
typedef struct DocInfo_t 
{
   
    std::string title;       // 文檔標(biāo)題
    std::string content;     // 文檔內(nèi)容
    std::string url;         // 該文檔在官網(wǎng)中的url
}DocInfo_t;


//先聲明
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文件
    //第一步:遞歸式的把src_path路徑下的每個html文件名帶路徑,保存到files_list中
    //為什么這樣做? 方便后期進(jìn)行一個一個的文件進(jìn)行讀取
    if(!EnumFile(src_path, &files_list)) //枚舉所有帶路徑的文件名
    {
   
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }    

    //第二步:文件內(nèi)容的解析
    std::vector<DocInfo_t> results;//存放每個文檔解析完的內(nèi)容-DocInfo_t結(jié)構(gòu)
    if(!ParseHtml(files_list, &results)){
    //本質(zhì)是讀取files_list路徑的文件內(nèi)容并且解析
        std::cerr << "parse html error" << std::endl;
        return 2;
    }
    //第三步: 把解析完畢的各個文件內(nèi)容寫入到output路徑對應(yīng)的文件
    if(!SaveHtml(results, output)){
   
        std::cerr << "sava html error" << std::endl;
        return 3;
    }
    return 0;
}
//第一個參數(shù):所有文件保存的路徑	第二個參數(shù):輸出型參數(shù),枚舉的文件名保存的位置
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
   
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);//定義一個path類型的對象,遍歷的時候就從這個路徑下開始

    //判斷root_path這個路徑是否存在,如果不存在,就沒有必要再往后走了
    if(!fs::exists(root_path))
    {
   
        //注意:這里不能直接打印root_path,因為它是用boost庫的filesystem定義的對象,是一個對象??!
        std::cerr << src_path << " not exists" << std::endl;
        return false;
    }

    //對文件遞歸遍歷,定義一個空的迭代器,用來進(jìn)行判斷遞歸結(jié)束
    fs::recursive_directory_iterator end;//迭代器是模擬指針的行為,這個空迭代器可以認(rèn)為是nullptr
    //從root_path開始遍歷  iter也是迭代器對象,用root_path構(gòu)造它
    for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
   
        //對文件進(jìn)行篩選,判斷文件是否是普通文件:html都是普通文件
        if(!fs::is_regular_file(*iter))
        {
    
            continue;//不是普通文件就不處理
        }
        //這個普通文件還必須以html結(jié)尾,判斷文件后綴
        //iter->path():返回當(dāng)前迭代器所在的路徑   extension方法:提取路徑的后綴
        if(iter->path().extension() != ".html"){
    //判斷 文件路徑名的后綴是否符合要求
            continue; 
        }
        //測試: 來到這里,說明當(dāng)前的路徑一定是一個合法的,以.html結(jié)束的普通網(wǎng)頁文件
        //std::cout << "debug: " << iter->path().string() << std::endl;
        
        //iter->path()得到的還是路徑對象, string():將對象所對應(yīng)的路徑以字符串的形式呈現(xiàn)出來
        files_list->push_back(iter->path().string()); //將所有帶路徑的html保存在files_list,方便后續(xù)進(jìn)行文本分析
    }
    return true;
}

//第一個參數(shù):經(jīng)過清洗之后的html文件屬性信息數(shù)組	第二個參數(shù):保存解析完畢的文件內(nèi)容
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)
{
   
    #define SEP '\3'  //分隔符
    //這里按照二進(jìn)制方式進(jìn)行寫入,但是用文本寫入也是可以的,但是這里因為有\(zhòng)3
    //二進(jìn)制寫的特點(diǎn)是:寫入的是什么,文檔里保存的就是什么,程序不會給我們做自動轉(zhuǎn)化
    std::ofstream out(output, std::ios::out | std::ios::binary);//因為是要輸出->所以是out,以二進(jìn)制形式寫
    if(!out.is_open()){
   //打開文件失敗
        std::cerr << "open " << output <

到了這里,關(guān)于【項目】Boost搜索引擎的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C++項目】boost搜索引擎

    【C++項目】boost搜索引擎

    boost官網(wǎng) Boost庫是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。 Boost庫由Boost社區(qū)組織開發(fā)、維護(hù)。其目的是為C++程序員提供免費(fèi)、同行審查的、可移植的程序庫。Boost庫可以與C++標(biāo)準(zhǔn)庫完美共同工作,并且為其提供擴(kuò)展功能。Boost庫使用Boost License來授權(quán)使用,根據(jù)該協(xié)議

    2023年04月16日
    瀏覽(28)
  • 基于boost庫的搜索引擎項目

    基于boost庫的搜索引擎項目

    boost庫是指一些為C++標(biāo)準(zhǔn)庫提供擴(kuò)展的程序庫總稱,但是boost網(wǎng)站中并沒有為我們提供站內(nèi)搜索功能,因此我們要想找到某一個類的用法還要一個個去找,因此我們這次的目的就是實(shí)現(xiàn)一個搜索引擎功能,提高我們獲取知識的效率 比如百度,谷歌,360等,這些都是大型的搜索

    2024年03月14日
    瀏覽(29)
  • 基于boost準(zhǔn)標(biāo)準(zhǔn)庫的搜索引擎項目

    基于boost準(zhǔn)標(biāo)準(zhǔn)庫的搜索引擎項目

    這是一個基于Web的搜索服務(wù)架構(gòu) 客戶端-服務(wù)器模型 :采用了經(jīng)典的客戶端-服務(wù)器模型,用戶通過客戶端與服務(wù)器交互,有助于集中管理和分散計算。 簡單的用戶界面 :客戶端似乎很簡潔,用戶通過簡單的HTTP請求與服務(wù)端交互,易于用戶操作。 搜索引擎功能 :服務(wù)器端的

    2024年04月27日
    瀏覽(15)
  • 【Boost搜索引擎項目】Day1 項目介紹+去標(biāo)簽和數(shù)據(jù)清洗框架搭建

    【Boost搜索引擎項目】Day1 項目介紹+去標(biāo)簽和數(shù)據(jù)清洗框架搭建

    ??歡迎來到C++項目專欄 ?????♀?作者介紹:前PLA隊員 目前是一名普通本科大三的軟件工程專業(yè)學(xué)生 ??IP坐標(biāo):湖北武漢 ?? 目前技術(shù)棧:C/C++、Linux系統(tǒng)編程、計算機(jī)網(wǎng)絡(luò)、數(shù)據(jù)結(jié)構(gòu)、Mysql、Python ?? 博客介紹:通過分享學(xué)習(xí)過程,加深知識點(diǎn)的掌握,也希望通過平臺能

    2024年03月23日
    瀏覽(26)
  • [C++項目] Boost文檔 站內(nèi)搜索引擎(5): cpphttplib實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)、html頁面實(shí)現(xiàn)、服務(wù)器部署...

    [C++項目] Boost文檔 站內(nèi)搜索引擎(5): cpphttplib實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)、html頁面實(shí)現(xiàn)、服務(wù)器部署...

    在前四篇文章中, 我們實(shí)現(xiàn)了從文檔文件的清理 到 搜索的所有內(nèi)容: 項目背景: ??[C++項目] Boost文檔 站內(nèi)搜索引擎(1): 項目背景介紹、相關(guān)技術(shù)棧、相關(guān)概念介紹… 文檔解析、處理模塊 parser 的實(shí)現(xiàn): ??[C++項目] Boost文檔 站內(nèi)搜索引擎(2): 文檔文本解析模塊parser的實(shí)現(xiàn)、如何對

    2024年02月13日
    瀏覽(21)
  • [C++項目] Boost文檔 站內(nèi)搜索引擎(2): 文檔文本解析模塊parser的實(shí)現(xiàn)、如何對文檔文件去標(biāo)簽、如何獲取文檔標(biāo)題...

    [C++項目] Boost文檔 站內(nèi)搜索引擎(2): 文檔文本解析模塊parser的實(shí)現(xiàn)、如何對文檔文件去標(biāo)簽、如何獲取文檔標(biāo)題...

    在上一篇文章中, 已經(jīng)從 Boost 官網(wǎng)獲取了 Boost 庫的源碼. 相關(guān)文章: ??[C++項目] Boost文檔 站內(nèi)搜索引擎(1): 項目背景介紹、相關(guān)技術(shù)棧、相關(guān)概念介紹… 接下來就要編寫代碼了. 不過還需要做一些準(zhǔn)備工作. 創(chuàng)建項目目錄 所有的項目文件肯定要在一個目錄下, 找一個位置執(zhí)行下

    2024年02月14日
    瀏覽(25)
  • [C++項目] Boost文檔 站內(nèi)搜索引擎(3): 建立文檔及其關(guān)鍵字的正排 倒排索引、jieba庫的安裝與使用...

    [C++項目] Boost文檔 站內(nèi)搜索引擎(3): 建立文檔及其關(guān)鍵字的正排 倒排索引、jieba庫的安裝與使用...

    之前的兩篇文章: 第一篇文章介紹了本項目的背景, 獲取了 Boost 庫文檔 ??[C++項目] Boost文檔 站內(nèi)搜索引擎(1): 項目背景介紹、相關(guān)技術(shù)棧、相關(guān)概念介紹… 第二篇文章 分析實(shí)現(xiàn)了 parser 模塊. 此模塊的作用是 對所有文檔 html 文件, 進(jìn)行清理并匯總 ??[C++項目] Boost文檔 站內(nèi)搜

    2024年02月07日
    瀏覽(91)
  • boost庫搜索引擎

    boost庫搜索引擎

    Gitee倉庫:boost庫搜索引擎 市面上有很多搜索引擎例如Google、百度、360等,這些都是特別大的項目。 對于個人學(xué)習(xí)我們可以寫一個 站內(nèi)搜索 ,這個搜索的內(nèi)容更加垂直,數(shù)據(jù)量更小,例如C++的文檔The C++ Resources Network Google搜索顯示內(nèi)容: 客戶端使用瀏覽器搜索向服務(wù)器發(fā)起

    2024年04月09日
    瀏覽(29)
  • boost 搜索引擎

    boost 搜索引擎

    done 公司:百度、搜狗、360搜索、頭條新聞客戶端 - 我們自己實(shí)現(xiàn)是不可能的! 站內(nèi)搜索:搜索的數(shù)據(jù)更垂直,數(shù)據(jù)量其實(shí)更小 boost的官網(wǎng)是沒有站內(nèi)搜索的,需要我們自己做一個 首先在用戶進(jìn)行搜索之前,在公司的服務(wù)器server上,內(nèi)存上有一個searcher服務(wù),而我們想進(jìn)行搜

    2024年02月11日
    瀏覽(18)
  • Boost搜索引擎

    Boost搜索引擎

    先說一下什么是搜索引擎,很簡單,就是我們平常使用的百度,我們把自己想要所有的內(nèi)容輸入進(jìn)去,百度給我們返回相關(guān)的內(nèi)容.百度一般給我們返回哪些內(nèi)容呢?這里很簡單,我們先來看一下. 這里我們簡單的說一下我們的搜索引擎的基本原理. 我們給服務(wù)器發(fā)起請求,例如搜索關(guān)鍵

    2024年01月19日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包