您的位置 首页 java

用 Java 写了一个搜索引擎系统,这个太强了

前言

咱们如果用我们的小服务器去搞 百度 搜狗 那种引擎肯定是不行的,内属于全站搜索,我们这里做一个站内搜索。这个还是可以的,就类似于我们对网站里的资源进行搜索。

一.搜索引擎怎么搜索

搜索引擎就像一个小蜜蜂每天不停的采摘蜂蜜,就是去爬虫各个网页,然后通过爬取之后建立索引,以供于我们去搜索。

这里我们可以使用 Python ,或者下载文档压缩包。这里我们下包把,快多了。本来想搞一个英雄联盟的,实在找不见,要是后续有老铁找到可以分享一下。

建议大家别爬虫(要不然被告了,不过我们学校的官网倒是可以随便爬,我们当时就是拿这个练手的) 为什么要用 索引 呢?

因为爬的数据太多了,不索引,难道我去遍历吗?时间复杂度太大了。

这里我们需要建立索引,索引分别为正排索引,和倒排索引。

LOL 举个例子吧,正排就相当于,我们提到 无极剑圣 的技能就可以联想到:

  • Q技能 阿尔法突袭
  • W技能 冥想
  • E技能 无双
  • R技能 高原血统

所以这是根据名字选技能

倒排索引 就是LOL里面谁有剑:

  1. 蛮王
  2. 无极剑圣
  3. 剑姬

所以这是根据特点选择英雄

二.模块划分

1.索引模块

1)扫描下载到的文档,分析内容,构建出,正排索引和倒排索引。并且把索引内容保存到文件中。

2)加载制作i好的索引。并提供一些API实现查正排和查倒排这样的功能。

2.搜索模块

1)调用索引模块,实现一个搜索的完整过程。

输入:用户的查询词 输出:完整的搜索结果

3.web模块

需要实现一个简单的web程序,能够通过网页的形式和用户进行交互。包含了前端和后端。

三. 怎么实现分词

分词的原理:

1.基于词库

尝试把所有的词都进行穷举,把这些结果放到词典文件中。

2.基于统计

收集到很多的 语料库 ,进行人工标注,知道了那些字在一起的概率比较大~

java 中能够实现分词的第三方工具也是有很多的

比如ansj(听说唱的兄弟可能听过ansj,哈哈)这个就是一个 maven 中央仓库的分词第三方库。

我们直接下载最新版本然后放入 pom .

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 = " 易大师 是一个有超高机动性的刺客、战士型英雄,擅长利用快速的打击迅速击溃对手,易大师一般打野和走单人路,作为无极 剑道 的最后传人,易可以迅速砍出大量伤害,同时还能利用技能躲避猛烈的攻击,避开敌人的集火。";
  List<Term> terms = ToAnalysis.parse(str).getTerms();
  for (Term term : terms) {
   System.out.println(term.getName());
  }
 }
}
  

四.文件读取

把刚刚下载好的文档的路径复制到 String 中并且用常量标记。

这一步是为了用遍历的方法把所有 html文件 搞出来,我们这里用了一个递归,如果是绝对路径,就填加到文件 链表 ,如果不是就递归,继续添加里面的值。

 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.根据路径,去枚举出所有的文件.(html);
   ArrayList< file > fileList=new ArrayList<>();
    enum File(INPUT_PATH,fileList);
   System.out.println(fileList);
   System.out.println(fileList.size());
   //2.针对上面罗列出的文件,打开文件,读取文件内容,并进行解析
   //3.把在内存中构造好的索引数据结构,保定到指定的文件中。
  }
  //第一个参数表示从哪里开始遍历 //第二个表示结果。
  private void enumFile(String inputPath,ArrayList<File>fileList){
   File rootPath=new File(inputPath);
   //listFiles 能够获取到一层目录下的文件
  File[] files= rootPath.listFiles();
   for(File f:files){
    //根据当前f的类型判断是否递归。
    //如果f是一个普通文件,就把f加入到fileList里面
    //如果不是就调用递归
    if(f.isDirectory()){
     enumFile(f.getAbsolutePath(),fileList);
    }else {
     fileList.add(f);
    }
   }
  }
 public static void main(String[] args) {
  //通过main方法来实现整个制作索引的过程
  Parser parser=new Parser();
  parser.run();
 }
}
  

我们尝试运行一下,这里的文件也太多了吧,而且无论是什么都打印出来了。所以我们下一步就是把这些文件进行筛选,选择有用的。

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

这个代码就是只是针对末尾为html的文件。下图就是展示结果。

4.1 打开文件,解析内容。

这里分为三个分别是解析Title,解析Url,解析内容Content

4.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);

 }
  

4.1.2解析Url操作

这里的url就是我们平时去一个浏览器输入一个东西下面会有一个url,这个url就是我们的绝对路径经过截取获得出我们的相对的目录,然后与我们的http进行拼接,这样就可以直接得到一个页面。

 private String parseUrl(File f) {
  String part1="";
  String part2=f.getAbsolutePath().substring(INPUT_PATH.length());
   return part1+part2;
 }
  

4.1.3解析内容

以<>为开关进行对数据的读取,以int类型读取,为什么要用int而不是 char 呢因为int类型读完之后就变成-1可以判断一下是否读取完毕。具体代码如下很容易理解。

 private String parseContent(File f) throws IO Exception  {
   //先按照一个一个字符来读取,以<>作为开关
  try(FileReader fileReader=new FileReader(f)) {
   //加上一个是否拷贝的开关.
    boolean  isCopy=true;
   //还需要准备一个结果保存
    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.根据路径,去枚举出所有的文件.(html);
   ArrayList<File> fileList=new ArrayList<>();
   enumFile(INPUT_PATH,fileList);
   System.out.println(fileList);
   System.out.println(fileList.size());
   //2.针对上面罗列出的文件,打开文件,读取文件内容,并进行解析
   for (File f:fileList){
    System.out.println("开始解析"+f.getAbsolutePath());
    parseHTML(f);
   }
   //3.把在内存中构造好的索引数据结构,保定到指定的文件中。
  }

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

 }
 private String parseUrl(File f) {
  String part1="";
   String part2=f.getAbsolutePath().substring(INPUT_PATH.length());
   return part1+part2;
 }
 private String parseContent(File f) throws IOException {
   //先按照一个一个字符来读取,以<>作为开关
  try(FileReader fileReader=new FileReader(f)) {
   //加上一个是否拷贝的开关.
   boolean isCopy=true;
   //还需要准备一个结果保存
   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);
  //解析出对应的url
   String url=parseUrl(f);
  //解析出对应的正文
  try {
   String content=parseContent(f);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
  //第一个参数表示从哪里开始遍历 //第二个表示结果。
  private void enumFile(String inputPath,ArrayList<File>fileList){
   File rootPath=new File(inputPath);
   //listFiles 能够获取到一层目录下的文件
  File[] files= rootPath.listFiles();
   for(File f:files){
    //根据当前f的类型判断是否递归。
    //如果f是一个普通文件,就把f加入到fileList里面
    //如果不是就调用递归
    if(f.isDirectory()){
     enumFile(f.getAbsolutePath(),fileList);
    }else {
     if(f.getAbsolutePath().endsWith(".html"))
     fileList.add(f);
    }
   }
  }
 public static void main(String[] args) {
  //通过main方法来实现整个制作索引的过程
  Parser parser=new Parser();
  parser.run();
 }
}  

小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

用 Java 写了一个搜索引擎系统,这个太强了

原文出处:www.shaoqun.com/a/1790520.html

文章来源:智云一二三科技

文章标题:用 Java 写了一个搜索引擎系统,这个太强了

文章地址:https://www.zhihuclub.com/183263.shtml

关于作者: 智云科技

热门文章

网站地图