SpringCloud(十)——ElasticSearch简单了解(一)初识ElasticSearch和RestClient

文章目录

  • 1. 初始ElasticSearch
    • 1.1 ElasticSearch介绍
    • 1.2 安装并运行ElasticSearch
    • 1.3 运行kibana
    • 1.4 安装IK分词器
  • 2. 操作索引库和文档
    • 2.1 mapping属性
    • 2.2 创建索引库
    • 2.3 对索引库的查、删、改
    • 2.4 操作文档
  • 3. RestClient
    • 3.1 初始化RestClient
    • 3.2 操作索引库
    • 3.3 操作文档

1. 初始ElasticSearch

1.1 ElasticSearch介绍

Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性,能使数据在生产环境变得更有价值。Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。

Elasticsearch是与名为Logstash的数据收集和日志解析引擎以及名为Kibana的分析和可视化平台一起开发的。这三个产品被设计成一个集成解决方案,称为“Elastic Stack”(以前称为“ELK stack”)。

Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。Elasticsearch是分布式的,这意味着索引可以被分成分片,每个分片可以有0个或多个副本。每个节点托管一个或多个分片,并充当协调器将操作委托给正确的分片。再平衡和路由是自动完成的。相关数据通常存储在同一个索引中,该索引由一个或多个主分片和零个或多个复制分片组成。一旦创建了索引,就不能更改主分片的数量。

Elasticsearch使用Lucene,并试图通过JSON和Java API提供其所有特性。它支持facetting和percolating,如果新文档与注册查询匹配,这对于通知非常有用。另一个特性称为“网关”,处理索引的长期持久性;例如,在服务器崩溃的情况下,可以从网关恢复索引。Elasticsearch支持实时GET请求,适合作为NoSQL数据存储,但缺少分布式事务。

ElasticSearch中有一些新的概念,这里我们对应于MySQL数据库中的一些概念来对其进行讲解,可能会有更好的效果。

MySQLElasticSearch说明
TableIndex索引,就是文档的集合,类似于数据库中的表
RowDocument文档,就是一条条的数据,类似数据库中的一行,文档都是JSON形式
ColumnField字段,就是JSON中的字段名,类似数据库中的列
SchemaMappingMapping是索引中文档的约束,例如字段类型约束,类似数据库的表结构
SQLDSLDSL是ElasticSearch提供的JSON风格的请求语句,用于操作ElasticSearch

1.2 安装并运行ElasticSearch

因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络:

docker network create es-net

然后使用elasticsearch的7.12.1版本的镜像,直接pull。

pull elasticsearch:7.12.1

如果需要运行es并进行单点部署,那么命令如下:

docker run -d \--name es \-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \-e "discovery.type=single-node" \-v es-data:/usr/share/elasticsearch/data \-v es-plugins:/usr/share/elasticsearch/plugins \--privileged \--network es-net \-p 9200:9200 \-p 9300:9300 \
elasticsearch:7.12.1

命令解释:

  • -e "cluster.name=es-docker-cluster":设置集群名称
  • -e "http.host=0.0.0.0":监听的地址,可以外网访问
  • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":前一个是设置初始堆的大小,后一个设置最大堆的大小
  • -e "discovery.type=single-node":非集群模式
  • -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录
  • -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录
  • -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录
  • --privileged:授予逻辑卷访问权
  • --network es-net :加入一个名为es-net的网络中
  • -p 9200:9200:端口映射配置,暴露的HTTP请求的端口
  • -p 9300:9300:端口映射配置,暴露ElasticSearch互联的端口

访问虚拟机地址的9200端口,如果出现以下页面,说明配置成功,
在这里插入图片描述

1.3 运行kibana

kibana可以给我们提供一个ElasticSearch的可视化界面,方便我们学习,运行如下代码表示运行一个kibana,

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601  \
kibana:7.12.1
  • -e ELASTICSEARCH_HOSTS=http://es:9200":设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch
  • --network es-net :加入一个名为es-net的网络中,与elasticsearch在同一个网络中
  • -p 5601:5601:端口映射配置

之后等待其部署完毕后,访问虚拟机的5601端口,发现乳腺的界面表示启动成功,
在这里插入图片描述

1.4 安装IK分词器

在ElasticSearch中,我们常常需要用到分词的操作,英文还好,其自带的就可以进行分词,但是中文,其只会按照逐字的方式对词进行划分,这显然是并不友好的,因此,我们需要安装一个专门的分词器来对中文进行分词。

安装IK分词器的步骤如下:

# 进入容器内部
docker exec -it es /bin/bash# 在线下载并安装
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip#退出
exit#重启容器
docker restart es

打开kibana中的目录,找到Dev tools,
在这里插入图片描述
在其中输入DSL查询语句进行分词。

# 测试分词器
POST /_analyze
{"text": "我们ikun不惹事,但也不怕事","analyzer": "ik_smart"#分词的模式
}

分词结果如下:
在这里插入图片描述
还有一种分词模式是 ik_max_word ,能够按照最细粒度去进行分词,更加占用内存空间。

2. 操作索引库和文档

2.1 mapping属性

mapping属性相当于就是数据库的字段约束,主要常用的mapping属性约束如下:

  • type:字段数据类型,常见的简单类型如下:
    • 字符串:text(客分词的文本),keyword(精确值,如国家、品牌,ip)
    • 数值: long, integer, short, byte, double, float
    • 布尔值:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

2.2 创建索引库

了解了mapping约束后,我们就可以开始创建索引了,创建索引库的语法如下:

PUT /索引库名

创建索引也就是创建每一个字段的约束条件,与数据库类似,我们创建一个名为 ikun 的索引,索引如下:

PUT /ikun
{"mappings": {"properties": {"info": {"type": "text","analyzer": "ik_smart"},"email": {"type": "keyword","index": false},"name": {"type": "object","properties": {"firstName": {"type": "keyword"},"lastName": {"type": "keyword"}}}}}
}

执行后显示的结果如下,表明创建成功:

{"acknowledged" : true,"shards_acknowledged" : true,"index" : "ikun"
}

2.3 对索引库的查、删、改

查询索引库的语法如下:

GET /索引库名

删除索引库的语法如下:

DELETE /索引库名

需要注意的是,索引库一经创建就不允许进行修改,但是,我们可以对原来的索引库进行新增,语法如下:

PUT /索引库名/_mapping 
{ "properties": { "新字段名":{ "type": "integer" } } 
}

2.4 操作文档

在索引库中插入文档相当于在数据库的表结构中增加一行数据。

新增文档的DSL语法如下:

POST /索引库名/_doc/文档id
{"字段1": "值1","字段2": "值2","字段3": {"子属性1": "值3","子属性2": "值4",}
}

文档id如果没有指定的话,会随机生成。

比如我们对上面创建的索引库进行新增如下:

POST /ikun/_doc/1
{"info": "我们ikun不惹事,但也不怕事","email": "snow@gmail.com","name": {"firstName": "i","lastName": "kun"}
}

查看文档的语法为:

GET /ikun/_doc/1

删除文档的语法为:

DELETE /ikun/_doc/1

修改文档有两种方法。

一种是全量修改,其会首先找到旧的文档,将旧的文档进行删除,然后将修改的再添加进去。如果旧的文档不存在,这种方法还是会进行新增。语法如下:

PUT /索引库名/_doc/1
{"字段1": "值1","字段2": "值2",
}

还有一种是增量修改,只会修改指定的字段,语法如下:

POST /索引库名/_update/文档id
{"doc": {"字段名": "新的值"}
}

比如我们修改上面的文档1的邮箱可以为:

POST /ikun/_update/1
{"doc": {"email": "snowsnow@gmail.com"}
}

3. RestClient

ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES,这里我们要学习的就是java中调用RestClient。

我们的数据库数据结构如下所示,
在这里插入图片描述
故我们构建索引库的代码如下:

PUT /hotel
{"mappings": {"properties": {"id": {"type": "keyword"},"name": {"type": "text","analyzer": "ik_max_word","copy_to": "all"},"address": {"type": "keyword","index": false},"price": {"type": "integer"},"score": {"type": "integer"},"brand": {"type": "keyword","copy_to": "all"},"city": {"type": "keyword"},"starName": {"type": "keyword"},"bussiness": {"type": "keyword","copy_to": "all"},"location": {"type": "geo_point"},"pic": {"type": "keyword","index": false},"all": {"type": "text","analyzer": "ik_max_word"}}}
}

在ElasticSearch中,对经纬度专门指定了一个结构 geo_point ,这里面能够存储经度和纬度的结构,除此之外,上面的 copy_to 字段是对字段进行联合索引的时候使用的。比如在上面我们需要对 name 属性和 brand 属性就行搜索,一般是先搜索符合的 name ,再到结果集里面搜索符合条件的 brand ,这样显然非常麻烦,而加入了一个 copy_to 字段后,便可以将该属性复制一份到 all 属性中,当然, all 属性并不存在与索引的 suorce 里面,此后,如果我们需要查询符合条件的 namebrand 时,只需要查询 all 属性即可。

那怎么在java中操作RestClient客户端呢?

3.1 初始化RestClient

首先是引入依赖,需要引入如下的依赖:

<properties><java.version>1.8</java.version><elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<dependencies><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version>
</dependencies>

由于SpringBoot在父maven中已经定义了ElasticSearch的版本号,所以改版本的时候需要在 properties 标签中覆盖父pom定义的版本号。

之后就是初始化RestClient了。

如果我们对每一个类都要创建和销毁RestClient客户端的话,那就显得太过麻烦了,我们可以将创建和销毁写作一个Ioc切面,在每一个Bean创建之前切入并创建客户端,在每一个Bean执行后切入并销毁客户端,具体代码如下:

public class HotelIndexTest {private RestHighLevelClient restHighLevelClient;@BeforeEachvoid setup(){this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.59.233:9200")));}@AfterEachvoid teardown() throws IOException {this.restHighLevelClient.close();}
}

3.2 操作索引库

  • 创建索引库
        @Testvoid createHotelIndex() throws IOException {//1.创建Request对象,索引坤名称为ikunCreateIndexRequest request = new CreateIndexRequest("ikun");//2.准备请求参数,即DSL语句,第一个参数为DSL语句,第二个参数指定为JSON形式request.source("{\n" +"  \"mappings\": {\n" +"    \"properties\": {\n" +"      \"kunName\": {\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_smart\"\n" +"      }\n" +"    }\n" +"  }\n" +"}", XContentType.JSON);//3.发送请求restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);}
    
  • 删除索引库
        @Testvoid DeleteHotelIndex() throws IOException {//1.创建Request对象DeleteIndexRequest request = new DeleteIndexRequest("ikun");restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);}
    
  • 判断索引库是否存在
    	@Testvoid ExistHotelIndex() throws IOException {//1.创建Request对象GetIndexRequest request = new GetIndexRequest("ikun");boolean exist = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);System.out.println(exist);}
    

3.3 操作文档

  • 增加文档

    如果我们需要用RestClient进行文档的增加,那么首先我们需要的就是类型转换,我们的文档内容肯定是从数据库中进行获取,但是,数据库中的数据与索引库的数据还是有一点不一样的,那就是经纬度。在数据库中,我们定义的是经度以及纬度,但是,在索引库中,我们定义的是一个数据结构 geo_point ,里面包含了经度以及纬度,所以,我们首先定义一个与索引库结构一致的类,如下:

    @Data
    @NoArgsConstructor
    public class HotelDoc {private Long id;private String name;private String address;private Integer price;private Integer score;private String brand;private String city;private String starName;private String business;private String location;private String pic;public HotelDoc(Hotel hotel) {this.id = hotel.getId();this.name = hotel.getName();this.address = hotel.getAddress();this.price = hotel.getPrice();this.score = hotel.getScore();this.brand = hotel.getBrand();this.city = hotel.getCity();this.starName = hotel.getStarName();this.business = hotel.getBusiness();this.location = hotel.getLatitude() + ", " + hotel.getLongitude();this.pic = hotel.getPic();}
    }
    

    之后,便可以读取数据库中的信息对索引库进行文档的增加了,增加的代码如下,

    @SpringBootTest
    public class HotelIndexTest {@Autowiredprivate IHotelService hotelService;private RestHighLevelClient restHighLevelClient;@Testvoid AddHotelDocument() throws IOException {//1.根据ID查询酒店数据Hotel hotel = hotelService.getById(61083L);//2.转换为文档类型HotelDoc hotelDoc = new HotelDoc(hotel);//3.准备Request对象,其参数只接受StringIndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());//4.准备JSON文档request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);//5.发送请求restHighLevelClient.index(request, RequestOptions.DEFAULT);}@BeforeEachvoid setup(){this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.59.233:9200")));}@AfterEachvoid teardown() throws IOException {this.restHighLevelClient.close();}
    }
    
  • 查询文档

    	@Testvoid FindHotelDocument() throws IOException {//1.准备Request对象,其参数只接受StringGetRequest request = new GetRequest("hotel", "61083");//2.发送请求得到响应GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);//3.解析相应结果,即将source字段解析为json格式的字符串String json = response.getSourceAsString();//4.将JSON格式的字符串解析为相应的对象HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);System.out.println(hotelDoc);}
    
  • 更新文档

        @Testvoid UpdateHotelDocument() throws IOException {//1.准备Request对象,其参数只接受StringUpdateRequest request = new UpdateRequest("hotel", "61083");//2.准备参数,特别注意,这里是逗号,没有冒号!!request.doc("price", "1001","startName", "四钻");//3.发送请求restHighLevelClient.update(request, RequestOptions.DEFAULT);}
    
  • 删除文档

        @Testvoid UpdateHotelDocument() throws IOException {//1.准备Request对象,其参数只接受StringDeleteRequest request = new DeleteRequest("hotel", "61083");//2.发送请求restHighLevelClient.delete(request, RequestOptions.DEFAULT);}
    
  • 批量新增数据

        @Testvoid AddMoreDocument() throws IOException {//1.批量查询数据库中的信息List<Hotel> hotels = hotelService.list();//2.创建RequestBulkRequest request = new BulkRequest();//3.准备参数,添加多个新增的Requestfor(Hotel hotel: hotels){HotelDoc hotelDoc = new HotelDoc(hotel);request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(JSON.toJSONString(hotelDoc),XContentType.JSON));}//4.发送请求restHighLevelClient.bulk(request, RequestOptions.DEFAULT);}
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70776.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络技术二十二:NATPPP

NAT 转换流程 产生背景 定义 分类 常用命令 PPP PPP会话建立过程 认证 PPP会话流程

第 3 章 栈和队列(循环队列的顺序存储结构实现)

1. 背景说明 和顺序栈相类似&#xff0c;在队列的顺序存储结构中&#xff0c;除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外&#xff0c; 尚需附设两个指针 front 和 rear 分别指示队列头元素及队列尾元素的位置。约定&#xff1a;初始化建空队列时&#x…

qt nodeeditor编译安装

目录 1. 下载源码 2. Qt creator编译源码 2.1 编译debug模式 &#xff08;MinGW&#xff09; 2.2 编译release模式 &#xff08;MinGW&#xff09; 1. 下载源码 https://github.com/paceholder/nodeeditor/archive/refs/tags/3.0.10.zip 2. Qt creator编译源码 解压文件…

Java 数据库改了一个字段, 前端传值后端接收为null问题解决

前端传值后端为null的原因可能有很多种&#xff0c;我遇到一个问题是&#xff0c;数据库修改了一个字段&#xff0c;前端传值了&#xff0c;但是后台一直接收为null值&#xff0c; 原因排查&#xff1a; 1、字段没有匹配上&#xff0c;数据库字段和前端字段传值不一致 2、大…

c语言 4.0

&#x1f482; 个人主页: 程序员爱摸鱼&#x1f91f; 版权: 本文由【程序员爱摸鱼】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注点赞收藏(一键三连)哦&#x1f485; 想寻找共同成长的小伙伴&#xff0c;可以互粉哦 &#x1f4ac;文章目录…

软件架构设计(六) 软件架构风格-MDA(模型驱动架构)

概念 模型驱动架构MDA, 全称叫做Model Driven Architecture。 Model:表示客观事物的抽象表示Architecture:表示构成系统的部件,连接件及其约束的规约Model Driven: 使用模型完成软件的分析,设计,构建,部署和维护等 开发活动MDA起源于分离系统规约和平台实现的思想。之前…

stable diffusion实践操作-复制-清空-保存提示词

系列文章目录 stable diffusion实践操作 stable diffusion实践操作-webUI教程 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、右上生成图标附近按钮介绍1. 箭头介绍&#xff08;复现别人的…

视频汇聚/视频云存储/视频监控管理平台EasyCVR启动时打印starting server:listen tcp,该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&#xff0c;…

JSON是什么?认识 json

目录 一 Json定义 二 Json语法规则 三 json 的代码演示 一 Json定义 轻量级的文本数据交换格式具有自我描述性【看到某个Json数据就能知道它的特点】比xml传输速度快【跨平台】 二 Json语法规则 &#xff08;1&#xff09;语法 两种数据结构&#xff1a;对象和数组 大括号…

Qt6中使用Qt Charts

官方文档&#xff1a;Qt Charts 6.5.2 如果你是使用 CMake 构建的&#xff0c;则应在 CMakeLists.txt 中添加如下两行代码&#xff1a; find_package(Qt6 REQUIRED COMPONENTS Charts)target_link_libraries(mytarget PRIVATE Qt6::Charts) 其中 mytarget 为你的项目名称。一共…

SpringBoot初级开发--服务请求(GET/POST)所有参数的记录管理(8)

服务端在定位错误的时候&#xff0c;有时候要还原现场&#xff0c;这就要把当时的所有入参参数都能记录下来&#xff0c;GET还好说&#xff0c;基本NGINX都会记录。但是POST的请求参数基本不会被记录&#xff0c;这就需要我们通过一些小技巧来记录这些参数&#xff0c;放入日志…

天津web前端培训班 前端是否适合零基础学?

随着HTML 5和ECMAScript 6的正式发布&#xff0c;大量的前端业务逻辑&#xff0c;极大地增加了前端的代码量&#xff0c;前端代码的模块化、按需加载和依赖管理势在必行&#xff0c;因此Web前端越来越被人们重视。 Web前端的就业前景 Web前端开发工程师薪资持续走高&#xff…

小红书下一个爆款趋势锁定 | 秋冬种草指南

今夏「多巴胺」风靡全网&#xff0c;现秋日又捎来一股名为「美拉德」的风……俨然一副“新晋顶流”的架势。 如何抓住这一新趋势&#xff1f;本期千瓜将锁定小红书下一个爆款内容——「美拉德」&#xff0c;剖析笔记与底层逻辑&#xff0c;助力品牌洞见先机&#xff0c;运筹布…

长胜证券:主板或以震荡整理为主 结构性行情持续

长胜证券指出&#xff0c;技术面看&#xff0c;沪指3150点上方谨慎看多&#xff0c;缩量横盘整理代表强势&#xff0c;向上打破需市场放量且权重配合&#xff0c;后市若打破并站稳3230点可视为强势回转行情开启&#xff0c;考虑到短期人民币弱势格局&#xff0c;主板或以震动整…

【PCL-11】提取平面上层的目标物,剔除平面下层目标物

因项目需求,需提取平面上的物体,不提取平面下的物体,尝试采用超体聚类+LCCP分割的方式,但由于上层点云模型一侧有空洞,导致分割效果不理想。 这里采用pcl::ExtractPolygonalPrismData类,实现平面上物体的提取。 pcl::ExtractPolygonalPrismData类是通过设定处于同一平面模…

React三属性之:props

作用 将父组件的参数传递给子组件 父组件 import ./App.css; import React from react; import PropsTest from ./pages/propsTest class App extends React.Component{render(){return(<div><h2>App组件</h2><PropsTest obj{{name:王惊涛,age:27}}>…

基于qt软件的网上聊天室软件

1.服务器: 1).功能: 用于创建一个客户端&#xff0c;通过文本编辑器来获得端口号&#xff0c;根据获得的端口号创建服务器&#xff0c;等待客户端连接 创建成功会提示服务器创建成功 在收到客户端发送的信息时&#xff0c;把这条信息发送给其他所有客户端&#xff0c;实现群…

【Linux-Day9-进程间通信】

进程间通信 前提引入&#xff1a; 我们之前接触过管道 | 将前一个命令的输出结果作为后一个命令的输入 如&#xff1a;ps | grep “sleep” 其实就是将 ps 的结果写入到一个区域&#xff0c;在从这个区域读出目标数据 有名管道 mkfifo 创建管道文件 : mkfifo fifo ->创…

单片机采集传感器数据(整形,浮点型)modbus上传

浮点型数据 占两个寄存器&#xff08;四个字节&#xff09; short 整形 占一个寄存器 &#xff08;两个字节&#xff09; 注意&#xff01;&#xff01;&#xff01;&#xff01; stm32 是小端模式&#xff0c;而modbus解析数据是大端模式 所以先发送高字节 如int a16777220…

selenium 自动化测试——环境搭建

安装python&#xff0c;并且使用pip命令安装 selenium pip3 install selenium 然后尝试第一次使用selenium 完成一个简单的测试自动化脚本 from selenium import webdriver from selenium.webdriver.common.by import By import timedriver webdriver.Chrome() driver.get(…