用 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中央仓库的分词第三方库。

image.png

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

test包里直接操作:我们使用这个测试代码直接搞。试一下这个包咋用。

image.png

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<>();
          enumFile(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();
    }
}

image.png

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

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

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

image.png

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="https://docs.oracle.com/javase/8/docs/api/";
      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 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 "";
    }

这一模块总的代码块如下:

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="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 {
          //先按照一个一个字符来读取,以<>作为开关
        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();
    }
}



相关文章
|
3月前
|
JavaScript Java 大数据
基于JavaWeb的销售管理系统设计系统
本系统基于Java、MySQL、Spring Boot与Vue.js技术,构建高效、可扩展的销售管理平台,实现客户、订单、数据可视化等全流程自动化管理,提升企业运营效率与决策能力。
|
4月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
272 7
|
5月前
|
存储 Java 数据库连接
java 初学者必看的系统知识结构图详解
本文详解Java知识结构图,涵盖Java语言基础、JVM原理、集合框架、并发编程、网络通信及主流框架(如Spring Boot、MyBatis),并结合学生信息管理系统实例,帮助初学者构建完整知识体系,提升实战开发能力。
184 0
|
2月前
|
移动开发 监控 小程序
java家政平台源码,家政上门清洁系统源码,数据多端互通,可直接搭建使用
一款基于Java+SpringBoot+Vue+UniApp开发的家政上门系统,支持小程序、APP、H5、公众号多端互通。涵盖用户端、技工端与管理后台,支持多城市、服务分类、在线预约、微信支付、抢单派单、技能认证、钱包提现等功能,源码开源,可直接部署使用。
240 24
|
2月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
303 8
|
6月前
|
数据采集 前端开发 JavaScript
产科专科电子病历系统基于Java开发,实现与HIS、LIS、PACS及区域妇幼信息平台的三级互联互通
产科专科电子病历系统基于Java开发,采用前后端分离架构(Vue+ElementUI前端,MySQL数据库),实现与HIS、LIS、PACS及区域妇幼信息平台的三级互联互通。系统涵盖患者全息视图、快速智能录入、检验检查模块、智能高危评估、异常值提醒及自我监测等功能,支持孕期时间轴和综合评估,自动归集数据并完成高危评分,助力产科数据标准化、结构化,以及临床保健工作的全程智能化管理。
185 1
|
2月前
|
安全 前端开发 Java
使用Java编写UDP协议的简易群聊系统
通过这个基础框架,你可以进一步增加更多的功能,例如用户认证、消息格式化、更复杂的客户端界面等,来丰富你的群聊系统。
178 11
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Java与生成式AI:构建内容生成与创意辅助系统
生成式AI正在重塑内容创作、软件开发和创意设计的方式。本文深入探讨如何在Java生态中构建支持文本、图像、代码等多种生成任务的创意辅助系统。我们将完整展示集成大型生成模型(如GPT、Stable Diffusion)、处理生成任务队列、优化生成结果以及构建企业级生成式AI应用的全流程,为Java开发者提供构建下一代创意辅助系统的完整技术方案。
227 10
|
2月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
456 4
|
2月前
|
机器学习/深度学习 分布式计算 Java
Java与图神经网络:构建企业级知识图谱与智能推理系统
图神经网络(GNN)作为处理非欧几里得数据的前沿技术,正成为企业知识管理和智能推理的核心引擎。本文深入探讨如何在Java生态中构建基于GNN的知识图谱系统,涵盖从图数据建模、GNN模型集成、分布式图计算到实时推理的全流程。通过具体的代码实现和架构设计,展示如何将先进的图神经网络技术融入传统Java企业应用,为构建下一代智能决策系统提供完整解决方案。
364 0