搜索引擎項目的一點總結


  前幾天剛做完一個搜索引擎的項目,趁着今天有時間,把做這個項目的一些關鍵點,包括一些關鍵的算法、思路都整理一下,做一個總結,方便日后溫習,也方便以后對這個項目的擴展(PS:博客園上的第一篇博客,排版可能不是太好,大家將就一下),廢話不多說,先上源碼:https://github.com/Jeysin/searchEngine

  •   並發服務器方案

  這個項目的整體框架部分采用的是Reactor+ThreadPool方案。說到Reactor+ThreadPool方案,簡單來說,其實就是一個線程作為IO線程,線程池內的線程做為計算線程,采用epoll實現多路復用。IO線程負責監聽TCP連接請求和接收客戶端發來的數據,用一個大的loop,如果是有新的連接請求,就調用accept,將新的文件描述符添加進epoll的監聽鏈表中;如果是客戶端發送過來的數據,就用bind將這些數據做為傳入的參數綁定到一個函數上,再將這個函數作為一個任務添加進線程池的任務隊列,這里不得不順帶提一下我的線程池的實現方法,我在這里采用了一種基於對象的線程池設計方法(關於面向對象和基於對象,見:http://blog.csdn.net/jiangxinnju/article/details/40163215),將線程池中任務隊列的元素設置為function類型,也就是說,將一個一個封裝好的函數作為線程池中的任務,線程池中的計算線程只要有空閑,就會執行事先已經綁定好參數的函數。基於這種線程池的實現方式,IO線程就可以很方便地將數據綁定到一個函數上,然后將函數交給線程池,計算線程拿到函數,執行計算任務,計算完以后,再將計算結果放到一個待發送隊列,用一個eventfd(事先在epoll中注冊好的)通知IO線程,IO線程收到通知以后去待發送隊列中取出計算結果,並將其發送給對應的客戶端。所以這里是用了一個eventfd來實現IO線程和計算線程的同步。至此,整個項目的網絡框架部分基本就是這樣了。

  •   業務邏輯部分

  既然是做搜索引擎,那么肯定涉及到搜索引擎的常用技術和原理。簡單來說,一個好的搜索引擎,首先需要一個好的爬蟲,能夠幫你把網絡上的各種網頁抓取到本地;能夠對網頁進行去重;考慮到時間或資源成本,還要求爬蟲能對網頁的重要性進行一定的判斷,即先抓取“重要”的網頁;還要能及時地更新已有的網頁…… 其次,一個搜索引擎還需要有一套行之有效的索引系統,能夠將搜索效率提高,這里涉及到太多東西,想深入了解請戳北大天網:https://gitee.com/lewsn2008/LBTSE。下面談談我的簡單實現。

  首先,手動下載rss網頁作為本地網頁庫(爬蟲就留到以后再寫吧),當然,如果想要網頁庫全一點的話,可以去搜狗實驗室獲取網頁資源:http://www.sogou.com/labs/resource/list_pingce.php。先用tinyxml2解析rss源文件,提取出有效信息,去除xml標記,建成一個網頁庫,網頁庫結構如下:

<doc>

  <docid>id</docid>

  <link>www.example.com</link>

  <description>summary</description>

  <content>...</constent>

</doc>

  網頁庫建好以后,用simhash算法進行網頁去重,關於simhash算法,詳見:https://yanyiwu.com/work/2014/01/30/simhash-shi-xian-xiang-jie.html。這里簡單描述一下:對於一篇網頁而言,抽取若干個特征詞和其對應的權重(權重依據TF-IDF算法得來,關於TF-IDF算法,詳見:http://www.ruanyifeng.com/blog/2013/03/tf-idf.html),生成若干(feature, weight)對,對於每一個feature,映射成64位(也就是8字節大小)的一個特征序列,對於特征序列上的每一位,是1,就記為+weight,是0, 就記為-weight。最后將一篇文章所有的特征序列加和,得到一個總的特征序列,對於這個總的特征序列的每一位,大於0就記為1,小於0就記為0,得到最終的特征序列。只要比較兩篇文章最終的特征序列對應位置上的0和1有多少個不同(海明距離),就知道兩篇文章是否相似了。一般海明距離小於3則認為兩篇文章相似。網頁去重完畢后,生成一個新的網頁庫文件和一個偏移庫文件(記錄下每篇網頁在網頁庫文件中的id號、起始位置和長度,這樣方便根據id號找到偏移信息然后直接獲取到網頁),接下來就到了搜索引擎最核心的一步:生成倒排索引。

  倒排索引其實就是記錄下每一個詞(中文詞或英文詞)對應的網頁id號,以及對於某一篇網頁而言這個詞的權重(依據TF-IDF算法計算而來)。先用cppjieba對網頁進行分詞,利用STL中的map可以很方便地插入網頁中的每個詞,插入的同時記下每個詞對應的網頁id號。到所有的詞插入完畢以后,就可以利用TF-IDF算法計算對於某一篇網頁而言,每個詞的權重。至此,倒排索引生成完畢,最后的數據結構應該是map<string, map<int, double>>,其中string為每個詞,int為包含這個詞的網頁的id,double記錄下對於某一篇網頁而言,這個詞的權重。最后提一下TF-IDF算法的公式:weight=TF*IDF=TF*log(N/DF),其中TF表示一篇網頁中某個詞的詞頻,N表示整個網頁庫的網頁總數,DF表示在整個網頁庫中,包含這個詞的網頁的篇數。所以最后得出的weight值與TF成正比,與DF成反比,即對於某一個詞和某一篇網頁而言,這個詞在這篇網頁中的詞頻越大、這個詞在別的網頁中出現得越少(網頁區分度越高),則認為這個詞在這篇網頁中的權重越大。

  倒排索引建好以后,一個簡單的搜索引擎基本上也成型了。下面只剩下兩個問題:

  1.利用倒排索引找到和用戶輸入的關鍵詞相匹配的網頁后,網頁怎么排序?

  2.對於一篇網頁,如何自動生成摘要返回給用戶?

  第一個問題,采用的策略是將用戶的輸入先分詞,再去停用詞,然后得到若干關鍵字,然后根據關鍵字去倒排索引中獲取若干map<int, double>,把他們都插入到一個新的map<int, double>中,插入的時候得分相加,所以這個map中的double記錄下的是每篇網頁的總得分,包涵的關鍵詞越多的網頁得分會越高,包涵的關鍵詞權重越大,對應的網頁得分也會越高,最后按網頁得分對網頁進行排序。第二個問題,如何自動生成摘要?詳見:http://www.ruanyifeng.com/blog/2013/03/automatic_summarization.html。我這里用一個簡單的方法:將一篇網頁文章拆分成句子,找到每個關鍵詞第一次和第二次出現的那些句子,拼接起來作為摘要。

  至此,一個簡單的搜索引擎打造完畢,貼上效果圖:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM