1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 8704字,需瀏覽 18分鐘

         ·

        2022-04-09 21:46

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

        作者:愛編程的快樂人

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


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


        搜索引擎怎么搜索


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


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


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


        為什么要用索引呢?


        因?yàn)榕赖臄?shù)據(jù)太多了,不索引,難道我去遍歷嗎?時(shí)間復(fù)雜度太大了。


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

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

        Q技能 阿爾法突襲

        W技能 冥想

        E技能 無雙

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

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


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

        1.蠻王

        2.無極劍圣

        3.劍姬

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


        模塊劃分


        1.索引模塊

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

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

        2.搜索模塊

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

        輸入:用戶的查詢詞

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


        3.web模塊


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

        包含了前端和后端。


        怎么實(shí)現(xiàn)分詞


        分詞的原理

        1.基于詞庫

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

        2.基于統(tǒng)計(jì)

        收集到很多的語料庫,進(jìn)行人工標(biāo)注,知道了那些字在一起的概率比較大~

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

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



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

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



        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 = "易大師是一個(gè)有超高機(jī)動性的刺客、戰(zhàn)士型英雄,擅長利用快速的打擊迅速擊潰對手,易大師一般打野和走單人路,作為無極劍道的最后傳人,易可以迅速砍出大量傷害,同時(shí)還能利用技能躲避猛烈的攻擊,避開敵人的集火。";        List<Term> terms = ToAnalysis.parse(str).getTerms();        for (Term term : terms) {            System.out.println(term.getName());        }    }}


        文件讀取


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

        這一步是為了用遍歷的方法把所有html文件搞出來,我們這里用了一個(gè)遞歸,如果是絕對路徑,就填加到文件鏈表,如果不是就遞歸,繼續(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(){ //整個(gè)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)容,并進(jìn)行解析 //3.把在內(nèi)存中構(gòu)造好的索引數(shù)據(jù)結(jié)構(gòu),保定到指定的文件中。 } //第一個(gè)參數(shù)表示從哪里開始遍歷 //第二個(gè)表示結(jié)果。 private void enumFile(String inputPath,ArrayList<File>fileList){ File rootPath=new File(inputPath); //listFiles 能夠獲取到一層目錄下的文件 File[] files= rootPath.listFiles(); for(File f:files){ //根據(jù)當(dāng)前f的類型判斷是否遞歸。 //如果f是一個(gè)普通文件,就把f加入到fileList里面 //如果不是就調(diào)用遞歸 if(f.isDirectory()){ enumFile(f.getAbsolutePath(),fileList); }else { fileList.add(f); } } } public static void main(String[] args) { //通過main方法來實(shí)現(xiàn)整個(gè)制作索引的過程 Parser parser=new Parser(); parser.run(); }}


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

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


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


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


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


        1.1解析Title


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


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

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


        1.2解析Url操作


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

        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)進(jìn)行對數(shù)據(jù)的讀取,以int類型讀取,為什么要用int而不是char呢因?yàn)閕nt類型讀完之后就變成-1可以判斷一下是否讀取完畢。


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

        private  String parseContent(File f) throws IOException {          //先按照一個(gè)一個(gè)字符來讀取,以<>作為開關(guān)        try(FileReader fileReader=new FileReader(f)) {            //加上一個(gè)是否拷貝的開關(guān).            boolean isCopy=true;            //還需要準(zhǔn)備一個(gè)結(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(){          //整個(gè)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)容,并進(jìn)行解析          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 { //先按照一個(gè)一個(gè)字符來讀取,以<>作為開關(guān) try(FileReader fileReader=new FileReader(f)) { //加上一個(gè)是否拷貝的開關(guān). boolean isCopy=true; //還需要準(zhǔn)備一個(gè)結(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){ //解析出標(biāo)題 String title=parseTitle(f); //解析出對應(yīng)的url String url=parseUrl(f); //解析出對應(yīng)的正文 try { String content=parseContent(f); } catch (IOException e) { e.printStackTrace(); } } //第一個(gè)參數(shù)表示從哪里開始遍歷 //第二個(gè)表示結(jié)果。 private void enumFile(String inputPath,ArrayList<File>fileList){ File rootPath=new File(inputPath); //listFiles 能夠獲取到一層目錄下的文件 File[] files= rootPath.listFiles(); for(File f:files){ //根據(jù)當(dāng)前f的類型判斷是否遞歸。 //如果f是一個(gè)普通文件,就把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方法來實(shí)現(xiàn)整個(gè)制作索引的過程 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 首個(gè)最新訪問版本發(fā)布,新特性搶先看

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

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

        瀏覽 41
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美美女高潮 | 中文字幕超碰在线播放 | 亚洲AV无码久久精品色无码蜜桃 | 97AV| 嫩草亚洲精品蜜桃天堂5g新91 | 日本三级三级 | 骚逼熟女 | 欧美V亚洲 | 日韩aV在我线一区二区三反 | 色欲毛片|