Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站

ElasticSearch 安装elasticsearch 官网:elastic.co
https://www.elastic.co/cn/downloads/elasticsearch
官网下载巨慢,下载
目录
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

启动,访问9200:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片


访问9200接口:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

安装elasticsearch-head: git clone git://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
npm install
npm run start
open http://localhost:9100/
存在一个9200和9100的跨域问题!
点击链接,报跨域的错!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

修改elasticsearch中config下的yaml配置文件,修改:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

重启,连接成功:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

把索引当做一个数据库!可以建立索引(库),文档(库中的数据!)
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

后面所有的查询,查询功能在Kibana中做!
了解ELK Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

安装Kibana Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

好处:ELK基本上都是拆箱即用
启动测试:点bin下的kibana
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

默认的kibana端口为5601
PS:注意elasticsearch和kibana的版本必须一致 否则出错!
开发工具:(Post curl head 谷歌浏览器插件测试)
汉化,修改kibana yaml中配置 重启,
ES核心概念

  1. 索引
  2. 字段类型(mapping)
  3. 文档(documents)
概述:
集群、节点、索引、类型、文档、分片、映射是什么?
elasticsearch是面向文档,关系型数据库和elasticsearch 客观的对比如下,一切都是JSON!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

类型示例:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

【Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站】Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

IK分词器 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

安装https://github.com/medcl/elasticsearch-analysis-ik/releases
放在elasticsearch的plugin(插件)下
重启观察ES
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

关于elasticsearch中的命令 如elasticsearch-plugin:
命令行输入:
elasticsearch-plugin list

在kibana中测分词器:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

我们输入 超级喜欢狂神或java
发现问题:狂神说被拆开了!
这种自己需要的词,需要自己加到我们的分词器字典中!
向ik分词器增加自己的配置
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

重启es、kibana
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

再次测试一下狂神说,看下效果!ik_max_word最细粒度划分
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

以后自定义dic、导入即可!
索引引擎里面最重要的首先就是分词
Rest风格说明 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

基础测试
  1. 创建一个索引!也是在kibana终端中试用版PUT命令
PUT /test1/type1/1{ "name": "狂神说", "age": "3" }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

完成了自动增加索引!数据也成功的添加了,这就是可以当做数据库看的原因。。
  1. 那么name字段用不用指定类型呢?毕竟我们关系型数据库 是需要指定类型的啊!
    • 字符串类型 text、keyword
    • 数值类型 long、integer、short、byte、double、float、scaled float
    • 日期类型date
    • te布尔值类型boolean
    • 二进制类型binary
    • 等等。。。
  2. 指定字段的类型——创建规则
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

GET命令得到库的信息:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

  1. 查看默认的信息
通过命令get _cat/可以获得es当前的很多信息!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

修改 提交还是使用PUT即可!然后覆盖!最新办法
曾经的办法:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

现在的方法 使用POST:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

删除索引——DELETE
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

通过DELETE命令实现删除、根据你的请求来判断是删除索引还是删除文档记录!
使用RESTFUL风格是我们ES推荐大家使用的!
关于文档的基本操作(重点): 基本操作:
添加数据 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

更新数据,将小明改成小红 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

PUT更新数据 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

version代表被改变的次数
Post,_update自由度更高,PUT必须一次性修改一个个体的全部内容,但是Post可以选择部分修改!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

这个和PUT无异,要在后加_update
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

简单的搜索: GET Kuangshen/user/_search?q=name:狂神说
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

简单的条件查询,可以根据默认的映射规则,产生基本的查询!
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

复杂操作搜索 select(排序,分页,高亮,模糊查询,精准查询)
GET kuangshen/_search

输出:
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "kuangshen", "_type" : "user", "_id" : "1", "_score" : 1.0, "_source" : { "name" : "狂神说", "age" : 23, "desc" : "一顿操作猛如虎,一看工资2500", "tags" : [ "技术宅", "温暖", "直男" ] } }, { "_index" : "kuangshen", "_type" : "user", "_id" : "2", "_score" : 1.0, "_source" : { "name" : "张三", "age" : 3, "desc" : "法外狂徒", "tags" : [ "交友", "旅游", "渣男" ] } }, { "_index" : "kuangshen", "_type" : "user", "_id" : "3", "_score" : 1.0, "_source" : { "name" : "李四", "age" : 30, "desc" : "不知道如何形容", "tags" : [ "篮球", "IT", "型男" ] } }, { "_index" : "kuangshen", "_type" : "user", "_id" : "4", "_score" : 1.0, "_source" : { "name" : "王五", "age" : 32, "desc" : "就是一个屌丝", "tags" : [ "羽毛球", "钢琴", "渣男" ] } }, { "_index" : "kuangshen", "_type" : "user", "_id" : "5", "_score" : 1.0, "_source" : { "name" : "狂神说Java8", "age" : 34, "desc" : "就是一个大帅哥", "tags" : [ "围棋", "小提琴", "暖男" ] } } ] } }

GET kuangshen/_search { "query": { "match": { "name": "狂神" } } }

输出:
{ "took" : 9, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.9034984, "hits" : [ { "_index" : "kuangshen", "_type" : "user", "_id" : "1", "_score" : 1.9034984, "_source" : { "name" : "狂神说", "age" : 23, "desc" : "一顿操作猛如虎,一看工资2500", "tags" : [ "技术宅", "温暖", "直男" ] } }, { "_index" : "kuangshen", "_type" : "user", "_id" : "5", "_score" : 1.6534033, "_source" : { "name" : "狂神说Java8", "age" : 34, "desc" : "就是一个大帅哥", "tags" : [ "围棋", "小提琴", "暖男" ] } } ] } }

hit: 索引和文档信息
查询的结果总数
然后就是查询出来的具体文档
数据中的东西都可以遍历出来
分数:通过score判断谁更加符合结果
指定字段查询:
GET kuangshen/_search { "query": { "match": { "name": "狂神" } }, "_source": ["name","desc"] }

之后使用java操作es,所有的方法和对象就是这里面的key!
复杂操作:
排序 order中的desc降序、asc升序; 按照age
GET kuangshen/_search { "query": { "match": { "name": "狂神" } } ,"sort": [ { "age": { "order": "desc" } } ] }

分页
GET kuangshen/_search { "query": { "match": { "name": "狂神" } } ,"sort": [ { "age": { "order": "desc" } } ], "from": 0, "size": 1 }

From:从第几个数据开始,返回多少条数据(单页面的数据)
数据下标还是从0开始的,和学的所有数据结构是一样的
/search/{current}/{pagesize}
布尔值查询 通过布尔值进行更加精确的查询:多条件精确查询
must命令(相当于mysql的and),即所有条件要同时符合;如果将must改为should(相当于or),则只要满足其一即可;
类似的 must not 查询不是。。
GET kuangshen/user/_search { "query": { "bool": { "must": [ { "match": { "name": "狂神说" } }, { "match": { "age": 23 } } ] } } }

过滤器(filter) 筛选age范围
GET kuangshen/user/_search { "query": { "bool": { "must": [ { "match": { "name": "狂神" } } ], "filter": [ { "range": { "age": { "gte": 10, "lte": 40 } } } ] } } }

  • gt 大于
  • gte 大于等于
  • lt 小于
  • lte 小于等于
匹配多个条件 匹配出tags里面只要包含有男的,同时按照上到下分值高到低排列
GET kuangshen/user/_search { "query": { "match": { "tags": "男 技术" } } }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

多个条件使用空格隔开
只要满足其中一个结果即可以被查出
这个时候可以通过分值基本的判断
精确查询 term查询是直接通过待排索引指定的词条进行精确的查找的!
关于分词:
term,直接查询精确的
match:会使用分词器解析!(先分析文档,然后再通过分析的文档进行查询!)
两个字段类型text keyword
#新建db PUT testdb { "mappings": { "properties": { "name":{ "type": "text" }, "desc":{ "type": "keyword" } } } } #插入两条数据 PUT testdb/_doc/1 { "name": "狂神说Java name", "desc": "狂神说Java name" } PUT testdb/_doc/2 { "name": "狂神说Java name", "desc": "狂神说Java name2" }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

GET _analyze { "analyzer": "keyword", "text": "狂神说Java name" } GET _analyze { "analyzer": "standard", "text": "狂神说Java name" }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

由于desc的type是keyword,当做整体去搜索了
总结:keyword字段类型不会被分词器解析!
多个值匹配的精确查询 精确查询多个值
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

高亮查询
GET kuangshen/user/_search { "query": { "match": { "name": "狂神说" } }, "highlight": { "pre_tags": "", "post_tags": "
", "fields": { "name": {} } } }

默认是标签
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

也可以是自定义标签:设置pre_tags、post_tags
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

  • 匹配
  • 按照条件匹配
  • 精确匹配
  • 区间范围匹配
  • 匹配字段过滤
  • 多条件查询
  • 高亮查询
集成Springboot 文档
https://www.elastic.co/guide/index.html
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

找原生依赖 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

初始化 Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

配置 新建一个empty project ,再创建普通模块
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

创建Springboot 模块:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

勾上依赖:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

由于刚刚建的空project,故要陪JDK环境
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

问题:一定要保证我们的导入依赖和我们的es版本一致
默认的导入依赖和我们本地的版本不一致!
可以自定义版本依赖,保证一致
新建config、ElasticSearchConfig.java`
package com.kuang.config; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //狂神的spring两步骤: //1.找对象 //2.放到spring中用 @Configuration public class ElasticSearchConfig {// @Bean public RestHighLevelClient restHighLevelClient(){ RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"), new HttpHost("localhost", 9201, "http"))); return client; } }

源码
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

具体测试es api:
package com.kuang; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import java.io.IOException; //es 高级客户端测试API @SpringBootTest class KuangshenEsApiApplicationTests { //面向对象来操作 @Autowired @Qualifier("restHighLevelClient")//这里的qualifier用来指定下面的client为原始的restHighLevelClient private RestHighLevelClient client; // 测试索引的创建 Request @Test void testCreateIndex() throws IOException { // 1.创建索引请求 相当于kibana中的PUT CreateIndexRequest request = new CreateIndexRequest("kuang_index"); // 2.执行创建请求IndicesClient ,请求后获得响应 CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT); System.out.println(createIndexResponse); }//测试获取索引判断是否存在某索引 @Test void testExistIndex() throws IOException { GetIndexRequest request = new GetIndexRequest("kuang_index"); boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); System.out.println(exists); } // 测试删除索引 @Test void testDeleteIndex()throws IOException { DeleteIndexRequest request = new DeleteIndexRequest("testdb2"); AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT); System.out.println(delete.isAcknowledged()); } }

本来应该是private RestHighLevelClient restHighLevelClient; 这里为了简便,所以用Qualifier来限定client为restHighLevelClient;
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

  • 创建索引
  • 判断索引是否存在
  • 删除索引
  • 创建文档
  • crud文档
创建文档 新建一个pojo,放入User.java
package com.kuang.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.stereotype.Component; @Data @AllArgsConstructor @NoArgsConstructor @Component public class User { private String name; private int age; }

由于要将我们的数据放入请求 json,故在pom中导入阿里巴巴fastjson
这里是将对象编写为Json,再放入es的request中
编写test类:
@Test void testAddDocument(){ //创建对象 User user = new User("狂神说",3); IndexRequest request = new IndexRequest("kuang_index"); // 规则 put /kuang_index/_doc/1 request.id("1"); request.timeout(TimeValue.timeValueSeconds(1)); request.timeout("1s"); //将我们的数据放入请求 json request.source(JSON.toJSONString(user), XContentType.JSON); //客户端发送请求 IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); System.out.println(indexResponse.toString()); // System.out.println(indexResponse.status()); //对应我们命令返回的状态 }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

获取文档
//获取文档的信息 @Test void testGetDocument() throws IOException { GetRequest getRequest = new GetRequest("kuang_index", "1"); GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); System.out.println(getResponse.getSourceAsString()); //打印文档的内容 System.out.println(getResponse); //返回的全部内容和命令是一样的 }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

更新文档信息
//更新文档的信息 @Test void testUpdateDocument() throws IOException { UpdateRequest updateRequest = new UpdateRequest("kuang_index","1"); updateRequest.timeout("1s"); User user = new User("狂神说Java", 18); updateRequest.doc(JSON.toJSONString(user),XContentType.JSON); UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT); System.out.println(updateResponse.status()); }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

删除文档信息
//删除文档记录 @Test void testDeleteRequest() throws IOException { DeleteRequest request = new DeleteRequest("kuang_index", "1"); request.timeout("1s"); DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT); System.out.println(deleteResponse.status()); }

批量插入
//批量插入数据 @Test void testBulkRequest() throws IOException{ BulkRequest bulkRequest = new BulkRequest(); bulkRequest.timeout("10s"); //数据量大的时候,秒数可以增加ArrayList userList = new ArrayList<>(); userList.add(new User("psz",11)); userList.add(new User("psz2",12)); userList.add(new User("psz3",13)); userList.add(new User("psz4",14)); userList.add(new User("psz5",15)); //批处理请求 for (int i = 0; i < userList.size(); i++) { bulkRequest.add( new IndexRequest("kuang_index") .id(""+(i+1)) .source(JSON.toJSONString(userList.get(i)),XContentType.JSON)); } //请求+获得响应 BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT); System.out.println(bulkResponse.hasFailures()); //返回false:成功 }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

查询 小技巧:
一般企业中,会把index名存在utils里面的ESconst.java文件中:
package com.kuang.utils; public class ESconst { public static final String ES_INDEX = "kuang_index"; }

// 查询 // SearchRequest 搜索请求 // SearchSourceBuilder 条件构造 // HighlightBuilder 构建高亮 // TermQueryBuilder 精确查询 // MatchAllQueryBuilder // xxx QueryBuilder 对应我们刚才看到的命令 @Test void testSearch() throws IOException { SearchRequest searchRequest = new SearchRequest(ESconst.ES_INDEX); // 构建搜索的条件 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // 查询条件,可以使用querybuilders工具类实现 // QueryBuilders.termQuery精确匹配 // QueryBuilders.matchAllQuery匹配所有 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "qinjiang1"); //MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); sourceBuilder.query(termQueryBuilder); //分页 //sourceBuilder.from(); //sourceBuilder.size(); // 设置查询的时间 希望在60s内查出 sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); searchRequest.source(sourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); System.out.println(JSON.toJSONString(searchResponse.getHits())); System.out.println("--------------------------------------"); for (SearchHit documentFields : searchResponse.getHits().getHits()) { System.out.println(documentFields.getSourceAsMap()); } }

JD商城实战 新建Springboot initializr项目
导入es、fastjson等pom下的依赖
爬虫
数据问题?数据库获取,消息队列中获取,都可以成为数据源,或者爬虫
爬取数据:(获取请求返回的页面信息,筛选出我们想要的数据就可以了)
jsoup包:用于解析网页,不能爬电影
新建一个utils包放网页解析的工具类
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

本质的请求是:
https://search.jd.com/Search?keyword=java
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

所有在Js中的方法这里都可以使用
package com.kun.utils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; public class HtmlParseUtil { public static void main(String[] args) throws IOException { // 获取请求 https://search.jd.com/Search?keyword=java String url = "https://search.jd.com/Search?keyword=java"; //解析网页 Jsoup返回的就是浏览器Document对象 Document document = Jsoup.parse(new URL(url), 30000); Element element = document.getElementById("J_goodsList"); System.out.println(element.html()); //获取所有的li元素/标签 Elements elements = element.getElementsByTag("li"); //获取元素中的内容eq获取当前第一个元素,获取src属性 for (Element el : elements) { //关于这种图片特别多的网站,所有的图片都是延迟加载的! String img = el.getElementsByTag("img").eq(0).attr("src"); String price = el.getElementsByClass("p-price").eq(0).text(); String title = el.getElementsByClass("p-name").eq(0).text(); System.out.println("==============================="); System.out.println(img); System.out.println(price); System.out.println(title); } }

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

输出结果
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

注意:
在图片较多的网站中,图片往往是延迟加载的,注意看图片的属性:
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

将获取到的元素 封装成对象,新建pojo,Content.java
package com.kun.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Content { private String title; private String img; private String price; //可以自己添加属性 }

再次封装工具类:
package com.kun.utils; import com.kun.pojo.Content; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; @Component //丢进Springboot中 public class HtmlParseUtil { public static void main(String[] args) throws Exception { new HtmlParseUtil().parseJD("java").forEach(System.out::println); }public List parseJD(String keywords) throws Exception { // 获取请求 https://search.jd.com/Search?keyword=java String url = "https://search.jd.com/Search?keyword=" + keywords; //解析网页 Jsoup返回的就是浏览器Document对象 Document document = Jsoup.parse(new URL(url), 30000); //所有你在js中可以使用的方法,这里都可以使用 Element element = document.getElementById("J_goodsList"); //System.out.println(element.html()); //获取所有的li元素/标签 Elements elements = element.getElementsByTag("li"); //封装对象 ArrayList goodsList = new ArrayList(); //获取元素中的内容eq获取当前第一个元素,获取src属性 for (Element el : elements) { //关于这种图片特别多的网站,所有的图片都是延迟加载的! String img = el.getElementsByTag("img").eq(0).attr("src"); String price = el.getElementsByClass("p-price").eq(0).text(); String title = el.getElementsByClass("p-name").eq(0).text(); Content content = new Content(); content.setImg(img); content.setPrice(price); content.setTitle(title); goodsList.add(content); //System.out.println("==============================="); //System.out.println(img); //System.out.println(price); //System.out.println(title); } return goodsList; } }

编写业务层service:
package com.kun.service; import com.alibaba.fastjson.JSON; import com.kun.pojo.Content; import com.kun.utils.HtmlParseUtil; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import java.util.List; //业务编写 @Service public class ContentService { @Autowired private RestHighLevelClient restHighLevelClient; //1.解析数据放入es索引中 public Boolean parseContent(String keywords) throws Exception { List contents = new HtmlParseUtil().parseJD(keywords); //把查询的数据放入我们的es中 BulkRequest bulkRequest = new BulkRequest(); bulkRequest.timeout("2m"); //批量插入 for (int i = 0; i < contents.size(); i++) { bulkRequest.add( new IndexRequest("jd_goods") .source(JSON.toJSONString(contents.get(i)), XContentType.JSON)); }BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); return !bulk.hasFailures(); } }

测试:由于这个文件中又Autowire,所以就算建了主函数psvm,也不能测,必须启动服务;
直接用controller来测:
package com.kun.controller; import com.kun.service.ContentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ContentController { @Autowired private ContentService contentService; @GetMapping("/parse/{keyword}") public Boolean parse(@PathVariable("keyword") String keywords) throws Exception { returncontentService.parseContent(keywords); } }

再在业务层中实现搜索功能:
// 2. 获取数据实现搜索功能 public List> searchPage(String keyword,int pageNo,int pageSize) throws IOException { if(pageNo<=1){ pageNo = 1; } //条件搜索 SearchRequest searchRequest = new SearchRequest("jd_goods"); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //分页 sourceBuilder.from(pageNo); sourceBuilder.size(pageSize); //精准匹配 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword); sourceBuilder.query(termQueryBuilder); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); //执行搜索 searchRequest.source(sourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); //解析结果 ArrayList> list = new ArrayList<>(); for (SearchHit documentFields : searchResponse.getHits().getHits()) { list.add(documentFields.getSourceAsMap()); } return list; }

用Controller来测:
@GetMapping("/parse/{keyword}/{pageNo}/{pageSize}") public List> search(@PathVariable("keyword") String keyword, @PathVariable("pageNo")int pageNo, @PathVariable("pageSize") int pageSize) throws IOException { return contentService.searchPage(keyword, pageNo, pageSize); }

前后端分离
先在一个任意包下npm install vue生成vue文件,将内部一些js包导入Springboot项目中;axios.min.js; vue.min.js
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

在前端每个商品中得到result值
Java|ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站
文章图片

搜索高亮
修改业务层ContentService.java
//3. 新增高亮功能 public List> searchPageHighlightBuilder(String keyword,int pageNo,int pageSize) throws Exception { parseContent(keyword); if (pageNo <= 1) { pageNo = 1; } //条件搜索 SearchRequest searchRequest = new SearchRequest("jd_goods"); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //分页 sourceBuilder.from(pageNo); sourceBuilder.size(pageSize); //精准匹配 //TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword); //sourceBuilder.query(termQueryBuilder); //sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); //match匹配 可以支持中文搜索 MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("title", keyword); sourceBuilder.query(matchQueryBuilder); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); //超时 //高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("title"); //高亮的字段 highlightBuilder.requireFieldMatch(false); //如果一句里面有多个关键词高亮,则只显示第一个 highlightBuilder.preTags(""); highlightBuilder.postTags(""); sourceBuilder.highlighter(highlightBuilder); //执行搜索 searchRequest.source(sourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); //解析结果 ArrayList> list = new ArrayList<>(); for (SearchHit hit : searchResponse.getHits().getHits()) {Map, HighlightField> highlightFields = hit.getHighlightFields(); //获取到高亮字段 HighlightField title = highlightFields.get("title"); Map, Object> sourceAsMap = hit.getSourceAsMap(); //原来的结果!要在结果里面将高亮置换一下 //解析高亮的字段 将原来的字段换为我们高亮的字段即可 if (title != null) { Text[] fragments = title.fragments(); String n_title = ""; for (Text text : fragments) { n_title += text; } sourceAsMap.put("title", n_title); //高亮字段替换掉原来的内容即可! } list.add(sourceAsMap); } return list; }

效果

    推荐阅读