数据的获取
在开发项目之前,我们首先需要获取数据。我们可以从京东官网爬取一定的数据。
我们搜索Java之后可以发现他的地址实际上就是https://search.jd.com/Search?keyword=java
所以我们可以通过JSOUP对其进行解析,获取相关的数据。
相关依赖:
<!-- 解析网页-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
编写工具类
添加依赖之后,我们便可以来编写我们的工具类爬取相关数据。
在代码中,通过对html界面中各元素的分析,然后获取相关的数据,将其存入到es中。
通过对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 {
String url = "https://search.jd.com/Search?keyword=" + keywords;
Document document = Jsoup.parse(new URL(url), 30000);
Element element = document.getElementById("J_goodsList");
Elements elements = element.getElementsByTag("li");
List<Content> goodsList = new ArrayList<>();
for (Element el : elements) {
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;
}
}
业务代码
工具类完成之后,我们便可以进行业务代码的编写了。
配置文件
首先添加配置文件,将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;
public Boolean parseObject(String keywords) throws IOException {
List<Content> contents = new HtmlParseUtil().parseJD(keywords);
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可以看到数据插入成功。
查询数据
数据成功插入之后,我们便可以直接在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
获取数据成功。
前端代码
首先需要准备vue.js以及axios.js;
我们可以直接创建一个目录,然后执行相关VUE语句即可。
比如我创建一个test目录,然后进入目录打开cmd开始执行语句;
npm init
npm install axios
npm install vue
然后便可以在对应的目录中获取文件。
获取数据之后将其放入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>
界面展示:
高亮展示
前后端实现时候,一个大致的商城界面便出现了。还剩最后一步我们可以优化以下,就是搜索之后的高亮展示。
高亮展示其实很简单,我们只需要在查询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>
总结
以上便是一个完整的ES项目,在以后如果遇到需要大量查询数据的时候,我们便可以将数据从数据库中读取出来放入ES中,然后通过ES进行搜索,这样会极大的提高性能。
项目地址:
ed-study
|