<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          擼了個搜索引擎系統(tǒng),爽!

          共 8704字,需瀏覽 18分鐘

           ·

          2022-04-09 21:46

          上一篇:字節(jié)跳動面試經(jīng)驗總結(jié),已順利拿到offer!

          作者:愛編程的快樂人

          來源:blog.csdn.net/m0_57315623/article/details/123829698


          如果用我們的小服務(wù)器去搞百度,搜狗那種引擎肯定是不行的,內(nèi)屬于全站搜索,我們這里做一個站內(nèi)搜索。這個還是可以的,就類似于我們對網(wǎng)站里的資源進行搜索。


          搜索引擎怎么搜索


          搜索引擎就像一個小蜜蜂每天不停的采摘蜂蜜,就是去爬蟲各個網(wǎng)頁,然后通過爬取之后建立索引,以供于我們?nèi)ニ阉鳌?/span>


          這里我們可以使用Python,或者下載文檔壓縮包。這里我們下包把,快多了。本來想搞一個英雄聯(lián)盟的,實在找不見,要是后續(xù)有老鐵找到可以分享一下。


          建議大家別爬蟲(要不然被告了,不過我們學(xué)校的官網(wǎng)倒是可以隨便爬,我們當時就是拿這個練手的)


          為什么要用索引呢?


          因為爬的數(shù)據(jù)太多了,不索引,難道我去遍歷嗎?時間復(fù)雜度太大了。


          這里我們需要建立索引,索引分別為正排索引,和倒排索引。

          拿LOL舉個例子吧,正排就相當于,我們提到無極劍圣的技能就可以聯(lián)想到

          Q技能 阿爾法突襲

          W技能 冥想

          E技能 無雙

          R技能 高原血統(tǒng)

          故根據(jù)名字選技能


          倒排索引就是LOL里面誰有劍

          1.蠻王

          2.無極劍圣

          3.劍姬

          故根據(jù)特點選擇英雄


          模塊劃分


          1.索引模塊

          1)掃描下載到的文檔,分析內(nèi)容,構(gòu)建出,正排索引和倒排索引。并且把索引內(nèi)容保存到文件中。

          2)加載制作i好的索引。并提供一些API實現(xiàn)查正排和查倒排這樣的功能。

          2.搜索模塊

          1)調(diào)用索引模塊,實現(xiàn)一個搜索的完整過程。

          輸入:用戶的查詢詞

          輸出:完整的搜索結(jié)果


          3.web模塊


          需要實現(xiàn)一個簡單的web程序,能夠通過網(wǎng)頁的形式和用戶進行交互。

          包含了前端和后端。


          怎么實現(xiàn)分詞


          分詞的原理

          1.基于詞庫

          嘗試把所有的詞都進行窮舉,把這些結(jié)果放到詞典文件中。

          2.基于統(tǒng)計

          收集到很多的語料庫,進行人工標注,知道了那些字在一起的概率比較大~

          java中能夠?qū)崿F(xiàn)分詞的第三方工具也是有很多的

          比如ansj(聽說唱的兄弟可能聽過ansj,哈哈)這個就是一個maven中央倉庫的分詞第三方庫。



          我們直接下載最新版本然后放入pom.xml里面

          test包里直接操作:我們使用這個測試代碼直接搞。試一下這個包咋用。



          import org.ansj.domain.Term;import org.ansj.splitWord.analysis.ToAnalysis;import java.util.List;public class TastAnsj {    public static void main(String[] args) {        String str = "易大師是一個有超高機動性的刺客、戰(zhàn)士型英雄,擅長利用快速的打擊迅速擊潰對手,易大師一般打野和走單人路,作為無極劍道的最后傳人,易可以迅速砍出大量傷害,同時還能利用技能躲避猛烈的攻擊,避開敵人的集火。";        List<Term> terms = ToAnalysis.parse(str).getTerms();        for (Term term : terms) {            System.out.println(term.getName());        }    }}


          文件讀取


          把剛剛下載好的文檔的路徑復(fù)制到String中并且用常量標記。

          這一步是為了用遍歷的方法把所有html文件搞出來,我們這里用了一個遞歸,如果是絕對路徑,就填加到文件鏈表,如果不是就遞歸,繼續(xù)添加里面的值。

          import java.io.File;import java.util.ArrayList;

          //讀取剛剛文檔public class Parser { private static final String INPUT_PATH="D:/test/docs/api"; public void run(){ //整個Parser類的入口 //1.根據(jù)路徑,去枚舉出所有的文件.(html); ArrayList<File> fileList=new ArrayList<>(); enumFile(INPUT_PATH,fileList); System.out.println(fileList); System.out.println(fileList.size()); //2.針對上面羅列出的文件,打開文件,讀取文件內(nèi)容,并進行解析 //3.把在內(nèi)存中構(gòu)造好的索引數(shù)據(jù)結(jié)構(gòu),保定到指定的文件中。 } //第一個參數(shù)表示從哪里開始遍歷 //第二個表示結(jié)果。 private void enumFile(String inputPath,ArrayList<File>fileList){ File rootPath=new File(inputPath); //listFiles 能夠獲取到一層目錄下的文件 File[] files= rootPath.listFiles(); for(File f:files){ //根據(jù)當前f的類型判斷是否遞歸。 //如果f是一個普通文件,就把f加入到fileList里面 //如果不是就調(diào)用遞歸 if(f.isDirectory()){ enumFile(f.getAbsolutePath(),fileList); }else { fileList.add(f); } } } public static void main(String[] args) { //通過main方法來實現(xiàn)整個制作索引的過程 Parser parser=new Parser(); parser.run(); }}


          我們嘗試運行一下,這里的文件也太多了吧,而且無論是什么都打印出來了。所以我們下一步就是把這些文件進行篩選,選擇有用的。

          else {                 if(f.getAbsolutePath().endsWith(",html"))                 fileList.add(f);             }


          這個代碼就是只是針對末尾為html的文件。下圖就是展示結(jié)果。


          1. 打開文件,解析內(nèi)容。


          這里分為三個分別是解析Title,解析Url,解析內(nèi)容Content


          1.1解析Title


          f.getName()是直接讀取文件名字的方法。


          我們用的name.substring(0,f.getName().length()-5);為什么要用總的文件名字長度減去5呢,因為.HTML剛好就是五。

          private  String parseTitle(File f) {          String name= f.getName();         return name.substring(0,f.getName().length()-5);
          }


          1.2解析Url操作


          這里的url就是我們平時去一個瀏覽器輸入一個東西下面會有一個url,這個url就是我們的絕對路徑經(jīng)過截取獲得出我們的相對的目錄,然后與我們的http進行拼接,這樣就可以直接得到一個頁面。

          private  String parseUrl(File f) {      String part1="https://docs.oracle.com/javase/8/docs/api/";      String part2=f.getAbsolutePath().substring(INPUT_PATH.length());          return part1+part2;    }


          1.3解析內(nèi)容


          以<>為開關(guān)進行對數(shù)據(jù)的讀取,以int類型讀取,為什么要用int而不是char呢因為int類型讀完之后就變成-1可以判斷一下是否讀取完畢。


          具體代碼如下很容易理解。

          private  String parseContent(File f) throws IOException {          //先按照一個一個字符來讀取,以<>作為開關(guān)        try(FileReader fileReader=new FileReader(f)) {            //加上一個是否拷貝的開關(guān).            boolean isCopy=true;            //還需要準備一個結(jié)果保存            StringBuilder content=new StringBuilder();            while (true){                //此處的read的返回值是int,不是char                //如果讀到文件末尾,就會返回-1,這是用int的好處;                int  ret = 0;                try {                    ret = fileReader.read();                } catch (IOException e) {                    e.printStackTrace();                }                if(ret==-1) {                        break;                    }                    char c=(char) ret;                    if(isCopy){                        if(c=='<'){                            isCopy=false;                            continue;                        }                        //其他字符直接拷貝                        if(c=='\n'||c=='\r'){                            c=' ';                        }                        content.append(c);                    }else{                        if(c=='>'){                            isCopy=true;                        }                    }            }
          return content.toString(); } catch (FileNotFoundException e) { e.printStackTrace(); } return ""; }


          這一模塊總的代碼塊如下:

          import java.io.File;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;//讀取剛剛文檔public class Parser {     private static final  String INPUT_PATH="D:/test/docs/api";      public  void run(){          //整個Parser類的入口          //1.根據(jù)路徑,去枚舉出所有的文件.(html);          ArrayList<File> fileList=new ArrayList<>();          enumFile(INPUT_PATH,fileList);          System.out.println(fileList);          System.out.println(fileList.size());          //2.針對上面羅列出的文件,打開文件,讀取文件內(nèi)容,并進行解析          for (File f:fileList){              System.out.println("開始解析"+f.getAbsolutePath());              parseHTML(f);          }          //3.把在內(nèi)存中構(gòu)造好的索引數(shù)據(jù)結(jié)構(gòu),保定到指定的文件中。      }

          private String parseTitle(File f) { String name= f.getName(); return name.substring(0,f.getName().length()-5);
          } private String parseUrl(File f) { String part1="https://docs.oracle.com/javase/8/docs/api/"; String part2=f.getAbsolutePath().substring(INPUT_PATH.length()); return part1+part2; } private String parseContent(File f) throws IOException { //先按照一個一個字符來讀取,以<>作為開關(guān) try(FileReader fileReader=new FileReader(f)) { //加上一個是否拷貝的開關(guān). boolean isCopy=true; //還需要準備一個結(jié)果保存 StringBuilder content=new StringBuilder(); while (true){ //此處的read的返回值是int,不是char //如果讀到文件末尾,就會返回-1,這是用int的好處; int ret = 0; try { ret = fileReader.read(); } catch (IOException e) { e.printStackTrace(); } if(ret==-1) { break; } char c=(char) ret; if(isCopy){ if(c=='<'){ isCopy=false; continue; } //其他字符直接拷貝 if(c=='\n'||c=='\r'){ c=' '; } content.append(c); }else{ if(c=='>'){ isCopy=true; } } }
          return content.toString(); } catch (FileNotFoundException e) { e.printStackTrace(); } return ""; } private void parseHTML (File f){ //解析出標題 String title=parseTitle(f); //解析出對應(yīng)的url String url=parseUrl(f); //解析出對應(yīng)的正文 try { String content=parseContent(f); } catch (IOException e) { e.printStackTrace(); } } //第一個參數(shù)表示從哪里開始遍歷 //第二個表示結(jié)果。 private void enumFile(String inputPath,ArrayList<File>fileList){ File rootPath=new File(inputPath); //listFiles 能夠獲取到一層目錄下的文件 File[] files= rootPath.listFiles(); for(File f:files){ //根據(jù)當前f的類型判斷是否遞歸。 //如果f是一個普通文件,就把f加入到fileList里面 //如果不是就調(diào)用遞歸 if(f.isDirectory()){ enumFile(f.getAbsolutePath(),fileList); }else { if(f.getAbsolutePath().endsWith(".html")) fileList.add(f); } } } public static void main(String[] args) { //通過main方法來實現(xiàn)整個制作索引的過程 Parser parser=new Parser(); parser.run(); }}


          感謝您的閱讀,也歡迎您發(fā)表關(guān)于這篇文章的任何建議,關(guān)注我,技術(shù)不迷茫!小編到你上高速。 
              · END ·
          最后,關(guān)注公眾號互聯(lián)網(wǎng)架構(gòu)師,在后臺回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全


          正文結(jié)束


          推薦閱讀 ↓↓↓

          1.救救大齡碼農(nóng)!45歲程序員在國務(wù)院網(wǎng)站求助總理!央媒網(wǎng)評來了...

          2.如何才能成為優(yōu)秀的架構(gòu)師?

          3.從零開始搭建創(chuàng)業(yè)公司后臺技術(shù)棧

          4.程序員一般可以從什么平臺接私活?

          5.37歲程序員被裁,120天沒找到工作,無奈去小公司,結(jié)果懵了...

          6.IntelliJ IDEA 2019.3 首個最新訪問版本發(fā)布,新特性搶先看

          7.這封“領(lǐng)導(dǎo)痛批95后下屬”的郵件,句句扎心!

          8.15張圖看懂瞎忙和高效的區(qū)別!

          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品 A片在线 | 午夜爱爱影院 | 成人免费A片在线观看直播96 | 亚洲天堂小说视频 | 青青操黄|