暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

实战: elasticsearch7.6.1版本+jsoup爬取京东商品数据并使用

码农Amg 2021-04-16
385

前言

本文的实战需求是:从京东网页上拉去对应【关键字】的数据且存放到elasticsearch(以下简称es)中,然后通过Java操作es进行精准、分页、高亮搜索

【注意】:没有了解es的,建议先去了解es的基本使用

本文是基于对【狂神说Java】ElasticSearch7.6.x最新完整教程通俗易懂 课程的一个总结和分享,感兴趣的朋友可以到bilibili搜索狂神说Java;

https://www.bilibili.com/video/BV17a4y1x7zq


目录

  1. 前期准备

  2. 数据准备

  3. 业务编写

  4. 前后端分离

  5. 总结


1、前期准备


  • elasticsearch7.6.1和head插件

  • 新建一个springboot项目


        点击finish,让项目自动导包即可

  • 导入相关依赖

  • 如果想自己添加依赖,可以创建maven空项目,将这一段粘进去即可

      <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.2.5.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
      </parent>
      <dependencies>

      <!-- jsoup解析网页 -->
      <dependency>
      <groupId>org.jsoup</groupId>
      <artifactId>jsoup</artifactId>
      <version>1.10.2</version>
      </dependency>
      <!-- jackson 用来把对象转换成json -->
      <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.10.2</version>
      </dependency>
      <!-- springboot集成elasticsearch -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
      </dependency>

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      </dependencies>
      复制


    • 修改spring默认的es版本,这很重要,不然连接会报错

    • 当前导入的spring版本是2.2.5,默认的es版本是6.8.6;而我们的es版本是7.6.1,所以需要修改过来

      pom文件指定es版本

         <properties>
        <elasticsearch.version>7.6.1</elasticsearch.version>
        </properties>
        复制
      • 将测试页面导入到项目,后续会使用到axios和vue,所以也把对应的js文件导入

      • 使用Java代码在ES中创建一个索引库

      Java中操作es的步骤都可以在es官网文档中找到,从官方文档里面得知,我们在Java中要使用es,需要有一个客户端,基于es的版本,我们选择高版本客户端

              

          所以,我们应该条件反射,写一个配置类,把创建es客户端的步骤交给spring托管

          

        /**
        * @Author: Amg
        * @Date: Created in 15:12 2021/04/11
        * @Description: TODO
        */
        @Configuration
        public class MyConfiguration {


        @Bean
        public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(new HttpHost("localhost", 9200, "http")));
        return client;
        }
        }
        复制

            然后创建一个索引

            

        ok~至此准备工作做好了,那么接下来就可以开干了!

        2、数据准备

        1. 使用Jsoup爬取京东网站数据

        2. 首先我们找到在京东上搜索java:https://search.jd.com/Search?keyword=java

        3. 然后再搜索vue:https://search.jd.com/Search?keyword=vue

        4. 再搜索apple:https://search.jd.com/Search?keyword=apple

        5. 可以观察到,url固定前缀https://search.jd.com/Search?keyword= ,后续会用到

        6. 然后打开F12,点击左上角小鼠标,定位到商品栏目信息


        通过上述观察,可以给出基本解析获取数据代码

          /**
          * @Author: Amg
          * @Date: Created in 15:13 2021/04/11
          * @Description: 商品信息实体类
          */
          @Data
          @NoArgsConstructor
          @AllArgsConstructor
          public class GoodsContent {

          private String price;

          private String title;

          private String img;
          }


          /**
          * @Author: Amg
          * @Date: Created in 15:27 2021/04/11
          * @Description: 使用Jsoup解析网页数据获取数据
          */
          public class ParseHtmlUtil {

          //这里只针对京东搜索,如果需要改变url,请自行去查看网页构造结构,然后修改结构;
          //固定的url,后续只需要拼接对应的关键字即可
          private static final String BASE_URL = "https://search.jd.com/Search?keyword=";

          //当然如果这么写,每次都只能解析第一页的数据,所以如果要获取不同页面的数据,就可以通过循环来操作,这里有兴趣的朋友可以自行改造
          // private static final String BASE_URL = https://search.jd.com/Search?keyword=java&page=5

          /**
          * 按照 【关键词】搜索,并且返回具体的数据
          * @param keyword 关键词
          * @return 一个装载GoodsContent的list
          */
          public static List<GoodsContent> parseHtml(String keyword) {

          String url = BASE_URL + keyword;

          List<GoodsContent> list = new ArrayList<>();
          try {

          //获取浏览器Document对象,设置超时时间为30s
          final Document document = Jsoup.parse(new URL(url), 30000);

          //通过此对象就可以解析网页标签结构,得到想要的信息;所以你需要做的就是分析网页结构

          //这就是刚刚我们定位到的【京东商品信息存放地方】
          final Elements elements = document.getElementById("J_goodsList").getElementsByTag("li");

          //进一步处理数据,把我们想要的数据给拿出来
          for (Element element : elements) {

          final String price = element.getElementsByClass("p-price").eq(0).text();
          final String title = element.getElementsByClass("p-name").eq(0).text();
          //图片如果直接找img src路径是获取不到的,因为类似京东这样子的网站,图片如此之多,肯定需要做懒加载,不然加载就会非常的慢
          //网页上在数据未加载完成的时候,可能只会存放一张静态的图片,等到数据获取回来,才会渲染上去,所以这里需要获取真正存放这个图片的路径
          final String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");

          GoodsContent goods = new GoodsContent();
          goods.setImg(img);
          goods.setTitle(title);
          goods.setPrice(price);

          list.add(goods);
          }

          } catch (Exception e) {
          System.err.println("爬去数据失败,请检查原因...");
          e.printStackTrace();
          }

          return list;
          }
          }
          复制


          3、业务编写

          把解析出来的数据放到es指定的索引库中

           
          复制
            /**
            * 把数据存放到ES指定的索引库中
            * @param keyword 商品关键字
            * @param idx 索引库
            * @return 插入状态
            */
            public Boolean setDataToEs(String keyword,String idx) throws IOException {

            final List<GoodsContent> list = ParseHtmlUtil.parseHtml(keyword);


            //Java里面使用BulkRequest对象来批量插入数据,所以我们先new一个
            BulkRequest bulkRequest = new BulkRequest();

            for (int i = 0; i < list.size(); i++) {
            //遍历插入json数据
            bulkRequest.add(new IndexRequest(idx).source(OBJECT_MAPPER.writeValueAsString(list.get(i)), XContentType.JSON));
            }

            final BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);

            //如果成功插入,返回true
            return !bulkResponse.hasFailures();
            }
            复制
             
            复制


            添加测试数据

            从指定索引库中搜索指定关键字的数据,实现分页,高亮显示

             
            复制
              /**
              * 从es中获取数据
              * @param keyword es中的title
              * @param pageNo 第几页
              * @param pageSize 每页大小
              * @param esIdx 从那个索引库中取数据
              * @return List<Map<String, Object>>
              */
              public List<Map<String, Object>> getDataFromEs(String keyword, int pageNo, int pageSize, String esIdx) throws IOException {

              if (pageNo <= 1) {
              pageNo = 1;
              }

              //构造搜索请求
              SearchRequest searchRequest = new SearchRequest(esIdx);

              //条件搜索
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

              //精准匹配 如果需要其他条件搜索,例如模糊,前缀匹配等等,修改这里的规则
              final TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
              searchSourceBuilder.query(termQueryBuilder);
              searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

              //分页
              searchSourceBuilder.from(pageNo);
              searchSourceBuilder.size(pageSize);

              //把title匹配的值进行高亮显示,并且设置高亮样式
              final HighlightBuilder highlightBuilder = new HighlightBuilder();
              highlightBuilder.field("title");
              highlightBuilder.preTags("<span style='color:red;font-size:15px;font-weight:bold'>");
              highlightBuilder.postTags("</span>");
              searchSourceBuilder.highlighter(highlightBuilder);


              //把搜索条件放到搜索请求中
              searchRequest.source(searchSourceBuilder);

              //调用客户端执行搜索
              final SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

              //通过搜索回来的请求获取解析数据,并且放到放回的list中
              List<Map<String,Object>> list = new ArrayList<>();
              for (SearchHit documentFields : searchResponse.getHits().getHits()) {

              final Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();

              final Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
              final Text[] texts = highlightFields.get("title").getFragments();
              //这里还需要额外处理高亮字段,让前面加的那段preTags和postTags生效(事实上现在的text,已经被标签给包裹起来了,只需要替换原来的值即可)
              String n_title = "";
              if (texts.length > 0) {

              for (Text text : texts) {
              n_title = text.string();
              }
              }
              sourceAsMap.put("title",n_title);
              list.add(sourceAsMap);
              }
              return list;
              }
              复制


              至此,es中也有了测试数据,而且测试获取数据接口能返回数据

              ⚠️注意观察,title现在是加上了span标签了


              4、前后端分离

              接口有了,数据有了,前台使用什么?Vue


              使用axios发送请求到后端接口api,这里方便操作,页码定死为1,而且查询数据为10条,主要是验证好不好使


              最终得到的效果



              5、总结

              项目本身复杂度不高,易上手,当然了,es更加高级的玩法,这里并没有完全展现出来,还需要不断的去学习摸索,如果有更加好玩的,我也会继续更新分享出来

              源码的获取,关注微信公众号 :码农Amg。回复:es实战,即可得到项目的所有源代码;最后感谢你的观看,我们下期再见!



              文章转载自码农Amg,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论