【文档搜索引擎】实现索引构建——解析标题、解析URL、解析正文

news/2024/11/29 23:47:03/

文章目录

  • 实现索引构建
  • 解析标题
    • getName () 和 getAbsolutePath () 的区别
    • 截掉 .html
    • 完整代码逻辑
  • 解析 URL
    • 实现 URL 拼接
    • 完整代码逻辑
    • 测试代码
  • 解析正文
    • 实现思路
    • 读取内容操作的实现
    • 完整代码逻辑
    • 测试代码

实现索引构建

  • 一条搜索信息,就包含了标题、描述、展示 URL。这些信息就来自于要解析的 HTML 在这里插入图片描述

  • 因此当前的解析 HTML 操作,就是要把这个 HTML 文件的标题、描述、URL 给获取到

    • 描述可以视为是正文的一段摘要
    • 因此要想得到描述,就得先得到整个正文
    • 所以我们先解析正文,后面再说描述

要实现这个功能,基本的框架为:

java">public void run(){  // 2. 针对上面罗列出的文件路径,打开路径,读取文件内容,进行解析,并构建索引 for(File f : fileList) {  System.out.println("开始解析: "+ f.getAbsolutePath());  // 通过这个方法来解析单个 HTML 文件  parseHTML(f);  }  
}
  • 实现这个功能,我们封装一个 parseHTML() 方法。此方法需要完成:
    1. 解析出 HTML 的标题
    2. 解析出 HTML 对应的 URL
    3. 解析出 HTML 对应的正文(有了正文才有后续的描述)
java">private void parseHTML(File f) {  // 1. 解析出 HTML 的标题  String title = parseTitle(f);  // 2. 解析出 HTML 对应的 URL    String url = parseUrl(f);  // 3. 解析出 HTML 对应的正文(有了正文才有后续的描述)  String content = parseContent(f);  
}
  • 由于代码比较复杂,我们将三个任务都分给不同的方法进行完成

整个过程为:

  1. 为了解析 HTML ,我们创建一个 parseHTML 方法
  2. 解析 HTML 之后,我们发现还要:
  3. 解析标题,我们又创建了一个 parseTitle 方法
  4. 解析 URL,我们又创建了一个 parseUrl 方法
  5. 解析正文,我们又创建了一个 parseContent 方法

解析标题

我们可以通过获取文件名,来获取具具体的标题信息

java">private String parseTitle(File f) {  f.getName();  
}

getName () 和 getAbsolutePath () 的区别

我们可以写个代码测试一下:

java">public class TestGetName {  public static void main(String[] args) {  File f = new File("D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\Java docs\\api\\java\\util\\ArrayList.html");  System.out.println(f.getAbsolutePath());  System.out.println(f.getName());  }  
}
/**
D:\My Computer\02_Stricky\02_Code\01 比特Java班资料\Java docs\api\java\util\ArrayList.html
ArrayList.html
*/
  • getAbsolutePath 得到的是完整路径
  • getName 得到的是完整路径最后的一截

截掉 .html

搜索结果的标题里面,是展示一个 ArrayList.html 好,还是展示 ArrayList 好?

  • 展示后者更好
  • 大家都是 html,加上也没什么意义
  • 各大搜索引擎里面的标题里面也没有 .html

所以我们就需要把当前得到的字符串进行截取,去掉后面的 .html 部分

  • 这里我们使用 substring() 方法

substring() 方法的两种版本

  1. 只传一个参数
  • begin 开始截取,一直到结尾
  1. 传两个参数
  • begin 开始截取,到 end 停止
  • 前闭后开

ArrayList.html

  • 总长度:14
  • .html 长度:5
    . 这个位置的下标,就是总长度 - “.html 的长度
  • 总长度 - 后半部分的长度 ==> 前半部分的长度 ==> 正是后半部分开始的第一个字符的下标
java">f.getName().substring(0, f.getName().length() - ".html".length())
  • .html 虽然是字符串常量,但是他同样也是一个 String 类型,所以可以用 .length 求长度

Java 中的计算长度,有多种不同的风格:

  • 针对数组:.length 属性
  • 针对字符串:.length() 方法
  • 针对 List 等集合:.size() 方法

完整代码逻辑

java">private String parseTitle(File f) {  String name = f.getName();  return name.substring(0, name.length() - ".html".length());  
}
  • 这样就可以直接通过文件名,获取到标题信息

解析 URL

  • 在这里插入图片描述

  • 在真实的搜索引擎中,展示 URL 和跳转 URL 是不同的 URL。但是我们当前情况就可以按照一个 URL 来处理

    • 使用一个 URL,既作为展示 URL,也作为点击 URL

对于各大搜索引擎来说:

  1. 广告结果的话,需要根据点击计费
  2. 自然点击结果的话,需要根据点击来优化用户体验

实现 URL 拼接

Java API 文档,存在两份:

  1. 线上文档:https://docs.oracle.com/javase/8/docs/api/index.html
  2. 线下文档:D:\My Computer\02_Stricky\02_Code\01 比特 Java 班资料\docs\api\index.html

我们所期望的结果就是:用户点击搜索结果的时候,就能够跳转到对应的线上文档的页面。

  • 我们最终的跳转 URL 以:https://docs.oracle.com/javase/8/docs/api/固定前缀,然后根据当前本地文档所在的路径,去和前缀进行拼接
  • 我们是可以通过 getAbsolutePath() 获取到本地文档路径的,形如 D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\java\\util\\ArrayList.html,然后把后半部分提取出来:java\\util\\ArrayList.html再和前面的固定前缀进行拼接
java">public class TestURL {  private static final String INPUT_PATH = "D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\";  public static void main(String[] args) {  File file = new File("D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\java\\util\\ArrayList.html");  // 先获取到一个固定的前缀  String part1 = "https://docs.oracle.com/javase/8/docs/api/";  String part2 = file.getAbsolutePath().substring(INPUT_PATH.length());  String result = part1 + part2;  System.out.println(result);  }  
}
//运行结果:
//https://docs.oracle.com/javase/8/docs/api/java\util\ArrayList.html
  • 浏览器自身有容错能力,虽然在拼接出的 URL 中既有 \ ,也有 /,但是仍然能正常访问

完整代码逻辑

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

测试代码

java">public class TestURL {  private static final String INPUT_PATH = "D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\";  public static void main(String[] args) {  File file = new File("D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\java\\util\\ArrayList.html");  // 先获取到一个固定的前缀  String part1 = "https://docs.oracle.com/javase/8/docs/api/";  String part2 = file.getAbsolutePath().substring(INPUT_PATH.length());  String result = part1 + part2;  System.out.println(result);  }  
}

解析正文

一个完整的 HTML 文件,包含了

  • HTML 标签
  • 内容(Java 文档)
    接下来,进行解析正文的操作,核心就是去掉 HTML 文件中的标签

实现思路

实现去标签,有很多方法:

  1. 可以通过正则表达式来实现这里的去标签操作

[!quote] 正则表达式

  • 可以认为是一种计算机中进行字符串匹配/处理的常见手段
  • 核心就是通过一些特殊符号来描述字符串的特征,然后看某个字符串是否符合这些特征

去除 HTML 标签这个环节中,虽然正则表达式可以解决问题,但是用起来很麻烦,因此我们可以使用更简单粗暴的方式来实现这里的逻辑

  1. 依次读取 HTML 中的每个字符,然后针对判定每个字符
    • 若是 <,那么就从这个位置开始,直到遇到 > 位置,都不把这些字符放在结果中
    • 若遇到的字符串不是 <,就直接把当前的字符拷贝到一个结果中(StringBuilder
    • 在期间我们可以弄一个标志位 flag,为 true 就拷贝,为 false 就不拷贝

万一内容中存在 < 或者 > 怎么办呢?

  • 不会出现这种情况
  • HTML 中要求,< 使用 &lt 来代替;> 使用 &gt 来代替

读取内容操作的实现

我们在读文件的时候,有的时候是按照“字节“来读取,有的时候是按照“字符“来读取。在 Java 标准库中,既提供了能够按照字节读取的类(FileInputStream),也提供了能按照字符来读取的类(FileReader

  • 此时我们是按照字符来读取的,所以使用 FileReader
java">public String parseContent(File f) { StringBuilder content = new StringBuilder();try(FileReader fileReader = new FileReader(f)) {   boolean isCopy = true;         while (true) {  int ret= fileReader.read();  if(ret == -1) {  break;  }  char c = (char)ret;  if(isCopy){  if(c == '<'){   isCopy = false;  continue;  }content.append(c);  }else {                 if(c == '>'){  isCopy = true;  }  }  }} catch (IOException e) {  e.printStackTrace();  }  return content.toString();   
}
  • 使用一个 StringBuilder 类型的变量 content 进行字符串的操作,方便后面进行字符拼接
    • 因为 StringBuilder 类型的变量直接使用 append() 方法就可以在原 content 后面加上字符
  • new fileReader 的操作放在 try 之后,可以省略关闭文件的操作
  • 在循环中,read() 的返回值
    • ret == -1 的时候,代表读取操作结束,直接跳出循环。read() 的返回类型为 int,就是为了方便判断何时读取结束(等于 -1 的时候)
    • 否则一直进行字符的读取操作,并且需要将 int 类型的 ret 强转为 char,好进行后续的字符操作
  • isCopy 是开关,用来控制是否进行 append 操作的
    • false(关锁):当识别到 < 的时候就关锁,关锁后一定要进行 continent 操作,跳出此次循环,不然就会恒执行 append 操作。
    • true(开锁):当识别到 > 的时候就开锁,进行字符的 append 操作
  • 最后要返回 content 里面的字符串

在这里插入图片描述

观察运行结果可以看到,正文里面包含了大量的换行操作。实际上当前获取到这个正文,目的是为了后面能够生成描述信息(一段话,肯定不能有空行)

  • 所以我们肯定要把空行给去掉
    我们只需要在 append 操作前面,加上一个处理换行操作的语句就可以了
java">if(c == '\n' || c == '\r'){// 为了去掉换行/回车,把换行/回车替换成空格即可c = ' ';
}

完整代码逻辑

java">public String parseContent(File f) { StringBuilder content = new StringBuilder();try(FileReader fileReader = new FileReader(f)) {   boolean isCopy = true;         while (true) {  int ret= fileReader.read();  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;  }  }  }} catch (IOException e) {  e.printStackTrace();  }  return content.toString();   
}

测试代码

java">public class TestParseContent {  public static void main(String[] args) throws FileNotFoundException {  Parser parser = new Parser();  File file = new File("D:\\My Computer\\02_Stricky\\02_Code\\01 比特Java班资料\\docs\\api\\java\\util\\ArrayList.html");  String result = parser.parseContent(file);  System.out.println(result);  }  
}

http://www.ppmy.cn/news/1551030.html

相关文章

【Leetcode Top 100】234. 回文链表

问题背景 给你一个单链表的头节点 h e a d head head&#xff0c;请你判断该链表是否为 回文链表&#xff08;回文 序列是向前和向后读都相同的序列&#xff09;。如果是&#xff0c;返回 t r u e true true&#xff1b;否则&#xff0c;返回 f a l s e false false。 数据…

《Python基础》之函数、模块与库

目录 简介 一、函数 1、数学类函数 2、聚合类函数 3、和进制相关的函数 4、字符类函数 5、类型转换相关函数 6、获取输出类函数 二、模块与库的使用方法 1、模块和库的导入方法 2、第三方模块的下载 下载方法 简介 在Python编程的世界中&#xff0c;函数、模块和库是…

深入解析音视频流媒体SIP协议交互过程

一、引言 在音视频流媒体传输过程中&#xff0c;SIP&#xff08;Session Initiation Protocol&#xff09;协议发挥着举足轻重的作用。本文将详细全面地介绍音视频流媒体传输中的SIP协议&#xff0c;包括其基本概念、交互过程、关键信令以及应用场景 二、SIP协议基本概念 1.…

vue3 element plus 把表格数据导出到excel

背景&#xff1a;需要把表格数据导出到excel 步骤&#xff1a; 1.安装 npm install --save xlsx file-saver2.在导出的页面 import FileSaver from file-saver import * as XLSX from xlsx3.使用(核心代码) <template><div class"index"><el-butto…

深度学习与持续学习:人工智能的未来与研究方向

文章目录 1. 持续学习与深度学习1.1 深度学习的局限1.2 持续学习的定义 2. 目标与心智2.1 奖励假说2.2 心智的构成 3. 对研究方法的建议3.1 日常写作记录3.2 中立对待流行趋势 1. 持续学习与深度学习 1.1 深度学习的局限 深度学习注重“瞬时学习”&#xff0c;如ChatGPT虽在语…

用户管理(MySQL)

目录 1用户管理&#xff08;MySQL&#xff09; 1.1 用户 1.1.1 用户信息 1.1.2 创建用户(后%是可以任意远端登录) 1.1.3 刷新一下 1.1.4 删除用户 1.1.5 修改用户密码 1.2 数据库的权限 1.2.1 登录创建用户 1.2.2给权限 1.2.2.1 把jj数据库中uu表的权限给woaini这个…

如何处理vue项目中的错误的?都有哪些错误类型?

在 Vue 项目中,错误的处理至关重要,它帮助开发者捕捉并修复应用中的问题。Vue.js 提供了多种机制来帮助我们处理和调试错误,包括 Vue 的内置错误处理机制和常见的 JavaScript 错误处理方式。本文将分析 Vue 中错误的分类以及如何在 Vue 项目中进行处理,结合源码来理解其内部…

Java基础 设计模式——针对实习面试

目录 Java基础 设计模式单例模式工厂模式观察者模式策略模式装饰器模式其他设计模式 Java基础 设计模式 单例模式 单例模式&#xff08;Singleton Pattern&#xff09; 定义&#xff1a;确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。适用场景&…