ES实战项目——仿京东商城

数据的获取

在开发项目之前,我们首先需要获取数据。我们可以从京东官网爬取一定的数据。

我们搜索Java之后可以发现他的地址实际上就是https://search.jd.com/Search?keyword=java

所以我们可以通过JSOUP对其进行解析,获取相关的数据。

image-20211204211643792

相关依赖:

<!--        解析网页-->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.2</version>
</dependency>

编写工具类

添加依赖之后,我们便可以来编写我们的工具类爬取相关数据。

在代码中,通过对html界面中各元素的分析,然后获取相关的数据,将其存入到es中。

image-20211204212336990

通过对HTML的解析,获取页面中的图片、名字以及价格,将其封装到对象中,最后返回商品集合即可。

输入的查询关键字如果是中文的话需要进行转义。

public class HtmlParseUtil {

    public static void main(String[] args) throws IOException {
        new HtmlParseUtil().parseJD("java").forEach(System.out::println);
    }

    public List<Content> parseJD(String keywords) throws IOException {
        //https://search.jd.com/Search?keyword=java
        // 前提,需要联网 ,ajax不能获取到!
        // 中文需要转义
        String url = "https://search.jd.com/Search?keyword=" + keywords;
        //解析网页(document就是浏览器页面对象)
        Document document = Jsoup.parse(new URL(url), 30000);
        //所有js中的操作都可以使用。
        Element element = document.getElementById("J_goodsList");
        //获取所有Li标签
        Elements elements = element.getElementsByTag("li");

        List<Content> goodsList = new ArrayList<>();
        //获取元素中的内容 这个的el就是每一个li标签
        for (Element el : elements) {
            //关于获取不到图片,由于是懒加载的,所以获取src是获取不到的
            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
            String price = el.getElementsByClass("p-price").eq(0).text();
            String name = el.getElementsByClass("p-name").eq(0).text();

            Content content = new Content();
            content.setPrice(price);
            content.setImg(img);
            content.setTitle(name);
            goodsList.add(content);
        }

        return goodsList;
    }
}

image-20211204213704286

业务代码

工具类完成之后,我们便可以进行业务代码的编写了。

配置文件

首先添加配置文件,将es和spring boot集成。

@Configuration
public class ElasticSearchClientConfig {

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

批量添加数据

这个时候开始编写对应业务类,我们通过工具类获取商品集合后,将数据批量插入到es中。

@Service
public class ContentService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //解析数据 放入 es索引中
    public Boolean parseObject(String keywords) throws IOException {
        List<Content> 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();
    }
}
@RestController
public class ContentController {

    @Autowired
    private ContentService contentService;

    @GetMapping("/parse/{keyword}")
    public Boolean parse(@PathVariable("keyword") String keywords) throws IOException {
        return contentService.parseObject(keywords);
    }
}

编写完上述代码之后,我们便可以启动项目进行测试。

访问该地址:http://localhost:9090/parse/java

然后查看es可以看到数据插入成功。

image-20211204215232406

查询数据

数据成功插入之后,我们便可以直接在es中进行查询了。

根据前端传入的关键字以及分页信息去es中查询符合的数据。

@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
public List<Map<String, Object>> search(@PathVariable("keyword") String keyword,
                                        @PathVariable("pageNo") int pageNo,
                                        @PathVariable("pageSize") int pageSize) throws IOException {
    return contentService.searchPage(keyword, pageNo, pageSize);
}
//获取数据
public List<Map<String, Object>> 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 termQuery = QueryBuilders.termQuery("title", keyword);
    sourceBuilder.query(termQuery);
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

    //执行搜索
    searchRequest.source(sourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    //解析结果
    List<Map<String, Object>> list = new ArrayList<>();
    for (SearchHit hit : searchResponse.getHits().getHits()) {
        list.add(hit.getSourceAsMap());
    }

    return list;
}

访问如下接口:http://localhost:9090/search/java/1/10

获取数据成功。

image-20211204221055679

前端代码

首先需要准备vue.js以及axios.js;

我们可以直接创建一个目录,然后执行相关VUE语句即可。

比如我创建一个test目录,然后进入目录打开cmd开始执行语句;

npm init
npm install axios
npm install vue

然后便可以在对应的目录中获取文件。

image-20211204221629008

获取数据之后将其放入static中的js目录中,接下来便可以编写前端代码了。

流程可以分为以下几步:

  • 对搜索框进行双向绑定
  • 点击搜索按钮后向后端发起请求
  • 循环展示数据

首先编写VUE代码:

new Vue({
    el: '#app',
    data: {
        keyword: '',//搜索关键字
        results: []//搜索结果
    },
    methods: {
        
    }
})

对搜索框进行双向绑定,以及绑定按钮事件:

<fieldset>
    <legend>天猫搜索</legend>
    <div class="mallSearch-input clearfix">
        <div class="s-combobox" id="s-combobox-685">
            <div class="s-combobox-input-wrap">
                <input v-model="keyword" type="text" autocomplete="off" value="dd"
                       id="mq"
                       class="s-combobox-input" aria-haspopup="true">
            </div>
        </div>
        <button type="submit" id="searchbtn" @click.prevent="searchKey">搜索</button>
    </div>
</fieldset>

编写触发事件:

searchKey() {
    let keyword = this.keyword;
    console.log(keyword);
    //对接后端代码
    axios.get('search/' + keyword + '/1/10').then(response => {
        console.log(response.data);
        this.results = response.data;//绑定数据
    })
}

循环展示:

<div class="product" v-for="result in results">
    <div class="product-iWrap">
        <!--商品封面-->
        <div class="productImg-wrap">
            <a class="productImg">
                <img :src="result.img">
            </a>
        </div>
        <!--价格-->
        <p class="productPrice">
            <em><b>¥</b>{{result.price}}</em>
        </p>
        <!--标题-->
        <p class="productTitle">
            <a> {{result.title}} </a>
        </p>
        <!-- 店铺名 -->
        <div class="productShop">
            <span>店铺: alibaba </span>
        </div>
        <!-- 成交信息 -->
        <p class="productStatus">
            <span>月成交<em>999笔</em></span>
            <span>评价 <a>3</a></span>
        </p>
    </div>
</div>

界面展示:

image-20211204224655126

高亮展示

前后端实现时候,一个大致的商城界面便出现了。还剩最后一步我们可以优化以下,就是搜索之后的高亮展示。

高亮展示其实很简单,我们只需要在查询es的时候,使用HighlightBuilder条件构造器即可,然后最后将高亮的数据替换到我们的返回结果中即可。

//高亮展示
public List<Map<String, Object>> searchPageHighLight(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 termQuery = QueryBuilders.termQuery("title", keyword);
    sourceBuilder.query(termQuery);
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

    //高亮设置
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("title");
    highlightBuilder.requireFieldMatch(false);//多个高亮显示
    highlightBuilder.preTags("<span style='color:red'>");
    highlightBuilder.postTags("</span>");
    sourceBuilder.highlighter(highlightBuilder);

    //执行搜索
    searchRequest.source(sourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    //解析结果
    List<Map<String, Object>> list = new ArrayList<>();
    for (SearchHit hit : searchResponse.getHits().getHits()) {

        //解析高亮的字段
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField title = highlightFields.get("title");
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
        //置换结果 将原来的字段换为高亮的字段。
        if (title != null) {
            Text[] fragments = title.getFragments();
            String n_title = "";
            for (Text fragment : fragments) {
                n_title += fragment;
            }
            sourceAsMap.put("title", n_title);//替换
        }
        list.add(sourceAsMap);
    }

    return list;
}

最后需要对前端进行小小的修改。这样才能对高亮的html进行解析。

<!--标题-->
<p class="productTitle">
    <a v-html="result.title"></a>
</p>

image-20211204230016899

总结

以上便是一个完整的ES项目,在以后如果遇到需要大量查询数据的时候,我们便可以将数据从数据库中读取出来放入ES中,然后通过ES进行搜索,这样会极大的提高性能。

项目地址:

ed-study

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>