1.什么是搜索引擎?
如图所示:
我用的是谷歌浏览器,但是我的搜索引擎可以跟换 。切换到bing主页
在搜索框中我们输入一段话,跳到一个带有搜索结果的页面如下:
搜索引擎的核心功能:查找用户输入的词/一句话 相关联的网页。
搜索结果页一条记录包含的信息如下:
当我们点击标题,会跳转到落地页,如下图:
相信大家对搜索引擎都有一定的了解了。
2.实现搜索引擎的核心思路:
搜索引擎的网页又该如何获取呢?
像百度,搜狗,这样的大型搜索引擎,每天会有很多爬虫去爬取大量网页,在存储起来。
我们这里先直接去官网下载jdk文档(之后抽取时间改进利用爬虫获取网页)。
用户输入查询词之后,如何去让查询词语和当前的这些网页进行匹配呢?
搜索引擎工作原理
利用这些网页(.html文件解析过后,生成的文档DocInfo)构建正排索引和倒排索引。
3.正排索引和倒排索引原理:
正排,倒排原理理解
1.正排索引:文档 id -> 文档内容 (1个id对应1个文档内容)
2.倒排索引:词 -> 和词相关联的文档id (1个词对应一个/多个文档id)
举个例子:
//正排索引
1 我上街买了一份炸鸡
2 我晚饭吃了一份牛肉
//倒排索引(先分词)
我 1,2
上街 1
买 1
了 1,2
一份 1,2
炸鸡 1
晚饭 2
吃 2
牛肉 2
4.我要把搜索引擎做到什么程度?
我要做一个像百度,搜狗,bing,这样的搜索引擎都属于“全站式搜索引擎”,搜索整个网上所有网站。百度为什么那么厉害,很重要一部分原因就是他的爬虫实现的好(吐槽:广告也很多)。
我们还是先做一个“站内搜索”,类似于哔哩哔哩里的搜索框,搜索站内的东西。
我们要做一个java,jdk8文档的搜索引擎,比如输入一个ArrayList,会在页面显示一系列关于ArrayList文档的信息,通过点击标题或者utl跳转到官网jdk8文档。
5.获取文档(网页)
Java Downloads | Oracle
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html
在对比看一下离线后的文档路径
file:///C:/Users/Administrator/Desktop/online_search_doc/jdk-8u341-docs-all/docs/api/java/util/ArrayList.html
本地链接和在线网站链接相比得出结论:
我们可以基于离线文档来制作索引,实现搜索 ,当用户在搜索结果页面点击具体的搜索结果的时候,就自动跳转到在线文档的页面。
6.模块粗略划分:
1.索引模块:
2.搜索模块:
3.web模块:
7.创建项目
用到的工具:
idea专业版2022.2.1
webstorm专业版2022.2.1
8.介绍一下什么是分词
用户在使用时侯,往往会输入一句话,比如:
会出现一下结果页:
用户输入的话被分成好多个词,每个词又对应很多文档id,所以结果页面会有很多条记录,每条记录都包括标题,url,描述这三个基本信息 。
我上街买了一份炸鸡 可分词为:
我
上街
买
了
一份
炸鸡
我们要使用第三方库ansj来进行分词。ansj地址
说一下分词的原理,有助于编写代码。
1.基于词库(跟不上时代发展,每隔一段时间会出现新词)
2.基于统计(该方法更加科学,用的多)
分词原理
写个测试类用一下ansj这个第三方库
说一下爆红的原因:
在分词的时候,会加载一些词典文件,通过这些词典文件能够加快分词速度和准确率,但是没有这些词典文件ansj仍然能快速分出词。 (可配置词典文件)
我们在来看一下分词结果:
9.实现索引模块Parser类的整体流程
10.递归枚举文件
import java.io.File;
import java.util.ArrayList;
public class Parser {
public static final String INPUT_PATH = "C:/Users/Administrator/Desktop/online_search_doc/jdk-8u341-docs-all/docs/api/";
public void run() {
//1.枚举出所有的.html文件,包括子目录中的文件
ArrayList filelist = new ArrayList<>();
enumFile(INPUT_PATH, filelist);
// System.out.println(filelist);
// System.out.println(filelist.size());
//2.针对上面罗列出的文档路径,打开文件,读取文件内容,进行解析,构建索引
//3.把在内存中构造好的索引数据结构,保存到指定的文件中
}
private void enumFile(String inputPath, ArrayList filelist) {
File rootPath = new File(inputPath);
File[] files = rootPath.listFiles();
for (File f : files) {
if(f.isDirectory()){
enumFile(f.getAbsolutePath(),filelist);
}else {
filelist.add(f);
}
}
}
public static void main(String[] args) {
Parser parser = new Parser();
parser.run();
}
}
现在有一个问题,我要的是html文件,其他文件例如css,js等其他文件是我不想要它出现在结果里边,用户也看不懂,所以filelist里边要去掉这些前端文件。
11.除去不是html的文件
我怎么实现?
我的思路是得到所有文件的目录,它的后缀不是html的就不加到那个filelist里边
private void enumFile(String inputPath, ArrayList filelist) {
File rootPath = new File(inputPath);
File[] files = rootPath.listFiles();
for (File f : files) {
if(f.isDirectory()){
enumFile(f.getAbsolutePath(),filelist);
}else {
if(f.getAbsolutePath().endsWith(".html")){
filelist.add(f);
}
}
}
}
这里我想说的是,后续的开发中会遇到各种各样的问题,但是只要思想不滑坡,办法总比困难多。
浩大的工程,都是由一个一个小小的函数堆积起来的。好了,现在我们把所有的html文件都加载到filelist里边了,现在的问题是,我怎么根据上边的路径打开文件去解析里边的文件呢?解析,我要解析什么?(1.标题,2.url,3.描述(描述是根据正文来的,所以因该是解析正文)),我为什么要这三个东西?我要把这三个东西用一个弄一个实体类来表示,即一个DocInfo,里边有id,title,url,content,然后通过一个ArrayList
12.解析html
我们打开文件发现html的名字和文件中的title里的名字相似,但是html的名字更加获取,代码实现上更加容易,我们就以html前边的单词作为标题 。
这样的话,我们解析html的标题的功能完成了。我们继续往下做。
13.解析url
我们所期望的结果就是,用户点击搜索结果,就能够跳转到对应的线上文档的页面。
思路:
把本地路径的后半段提取出来作为后缀 java/util/ArrayList.html
以在线路径前半部分为前缀 https://docs.oracle.com/javase/8/docs/api/
拼接后就是完整的url https://docs.oracle.com/javase/8/ docs/api/java/util/ArrayList.html
写一个测试代码:
import java.io.File;
public class TestUrl {
public static final String INPUT_PATH = "C:/Users/Administrator/Desktop/online_search_doc/jdk-8u341-docs-all/docs/api/";
public static void main(String[] args) {
File f = new File("C:/Users/Administrator/Desktop/online_search_doc/jdk-8u341-docs-all/docs/api/java/util/ArrayList.html");
String part1 = "https://docs.oracle.com/javase/8/docs/api/";
String part2 = f.getAbsolutePath().substring(INPUT_PATH.length());
System.out.println(part1+part2);
}
}
这里虽然是反斜杠,但是粘贴在浏览器上还能打开,体现出浏览器的鲁棒性。
我们优化一下吧,把\都替换成/.
具体方法:
注意:
虽然没看懂,但是能用!接下来我们开始解析正文,先讲讲解析正文的思路:
14.解析正文
我们先打开一个html文件观察观察
这里边有script的代码,还有css的代码style,还有html的标签
这里我们使用正则表达式去除一下这些标签
具体代码:
public String parseContent(File f) {
try (FileReader fileReader = new FileReader(f)) {
StringBuilder content = new StringBuilder();
while (true) {
int ret = fileReader.read();
if (ret == -1) {
break;
}
char c = (char) ret;
content.append(c);
}
String scriptRegex = "