ElasticSearchの包括的な実践
一、爬虫
JDでJavaコピーリンクを検索https://search.jd.com/Search?keyword=java&enc=utf-8
そのdivの下で本を表示するにはF12
各本の次の情報
jsoupの依存関係をインポートする
<!--解析网页jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<!--引入阿里巴巴的fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
utilsパッケージを作成し、HtmlParseUtil.javaクロールテストをビルドします
//测试数据
public static void main(String[] args) throws IOException, InterruptedException {
//获取请求
String url = "https://search.jd.com/Search?keyword=java";
// 解析网页 (Jsou返回的Document就是浏览器的Docuement对象)
Document document = Jsoup.parse(new URL(url), 30000);
//获取id,所有在js里面使用的方法在这里都可以使用
Element element = document.getElementById("J_goodsList");
//获取所有的li元素
Elements elements = element.getElementsByTag("li");
//用来计数
int c = 0;
//获取元素中的内容 ,这里的el就是每一个li标签
for (Element el : elements) {
c++;
//这里有一点要注意,直接attr使用src是爬不出来的,因为京东使用了img懒加载
String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
//获取商品的价格,并且只获取第一个text文本内容
String price = el.getElementsByClass("p-price").eq(0).text();
String title = el.getElementsByClass("p-name").eq(0).text();
String shopName = el.getElementsByClass("p-shop").eq(0).text();
System.out.println("========================================");
System.out.println(img);
System.out.println(price);
System.out.println(title);
System.out.println(shopName);
}
System.out.println(c);
}
テスト結果
問題なく結果が得られたら、このメソッドを使用するツールクラスとしてカプセル化します。
pojoエンティティクラスを作成します
Content.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Content {
private String title;
private String img;
private String price;
//可以自行添加属性
}
パッケージングツール
HtmlParseUtils.java
@Component
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&enc=utf-8
//前提需要连网
String url = "https://search.jd.com/Search?keyword=" + keywords + "&enc=utf-8";
//解析网页 (Jsoup返回Document就是浏览器Document对象)
Document document = Jsoup.parse(new URL(url), 30000);
//所有在js中能使用的方法,这里都能用
Element element = document.getElementById("J_goodsList");
//获取所有li元素
Elements elements = element.getElementsByTag("li");
ArrayList<Content> goodList = new ArrayList<>();
//通过元素中的内容,这里el就是每一个li标签了
for (Element el : elements) {
//加if判断是为了 过滤空标签
if (el.attr("class").equalsIgnoreCase("gl-item")) {
//关于这种图片特别多的网页,所有的图片都是延迟加载的
//在jd搜索后f12可以看到存放在data-lazy-img中
String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
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);
goodList.add(content);
}
}
return goodList;
}
}
ビジネスレイヤコードを記述し、インターフェイスを削除します
ContentService.java
まず、クロールされたデータをESに保存する方法を完了します
//业务编写
@Service
public class ContentService {
//将客户端注入
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//1、解析数据放到 es 中
public boolean parseContent(String keyword) throws IOException {
List<Content> contents = new HtmlParseUtil().parseJD(keyword);
//把查询的数据放入 es 中
BulkRequest request = new BulkRequest();
request.timeout("2m");
for (int i = 0; i < contents.size(); i++) {
request.add(
new IndexRequest("jd_goods")
.source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
}
BulkResponse bulk = client.bulk(request, RequestOptions.DEFAULT);
return !bulk.hasFailures();
}
}
コントローラパッケージの下でビルドする
ContentController.java
//请求编写
@RestController
public class ContentController {
@Autowired
private ContentService contentService;
@GetMapping("/parse/{keyword}")
public Boolean parse(@PathVariable("keyword") String keyword) throws IOException {
return contentService.parseContent(keyword);
}
}
Springbootプロジェクトを開始して、データをESにクロールし
て検索機能を実現できるかどうかにアクセスします
。ContentService.javaに追加します。
//2、获取这些数据实现基本的搜索功能
public List<Map<String, Object>> searchPage(String keyword, int pageNo, int pageSize) throws IOException {
if (pageNo <= 1) {
pageNo = 1;
}
if (pageSize <= 1) {
pageSize = 1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(pageNo).size(pageSize);
//精准匹配
TermQueryBuilder termQuery = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQuery);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
SearchRequest source = searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//解析结果
List<Map<String, Object>> list = new ArrayList<>();
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
list.add(documentFields.getSourceAsMap());
}
return list;
}
ContentController.javaに検索リクエストを追加し、RestFulを使用します
@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 {
List<Map<String, Object>> list = contentService.searchPage(keyword, pageNo, pageSize);
return list;
}
http:// localhost:9090 / search / java / 1/20にアクセスします(Javaをクエリし、最初から20番目まで表示します)
。このデータのクロールと検索に問題はありません。ここで、の分離の前後から開始します。終わりは働いています
2、フロントとリアの分離
最初に準備されたリソースをインポートし、axios、jquery、vueのjsパッケージをインポートします。
フロントエンドはデータを受け入れる必要があります
vueでデータを受信する
<!--前端使用vue完成前后端分离-->
<script th:src="@{/js/axios.min.js}"></script>
<script th:src="@{/js/vue.min.js}"></script>
<script>
new Vue({
el: '#app',
data: {
keyword: '', //搜索的关键字
result: [] //搜索的结果
},
methods: {
searchKey() {
var keyword = this.keyword
axios.get('search/' + keyword + '/1/210').then(response => {
//console.log(response);
this.result = response.data;//绑定数据!
})
}
}
})
</script>
次に、ボタンクリックイベントと検索バインディングをバインドし、
vueを使用してデータをフロントエンドに渡し、
localhost:9090にアクセスし、Javaを検索して試してください
3、検索のハイライト
ContentService.javaの検索機能を変更します
//3、获取这些数据实现基本的搜索高亮功能
public List<Map<String, Object>> searchPagehighlighter(String keyword, int pageNo, int pageSize) throws IOException {
if (pageNo <= 1) {
pageNo = 1;
}
if (pageSize <= 1) {
pageSize = 1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from(pageNo).size(pageSize);
//精准匹配
TermQueryBuilder termQuery = QueryBuilders.termQuery("title", keyword);
//==================================== 高 亮 ==========================================
HighlightBuilder highlightBuilder = new HighlightBuilder(); //获取高亮构造器
highlightBuilder.field("title"); //需要高亮的字段
highlightBuilder.requireFieldMatch(false);//不需要多个字段高亮
highlightBuilder.preTags("<span style='color:red'>"); //前缀
highlightBuilder.postTags("</span>"); //后缀
sourceBuilder.highlighter(highlightBuilder); //把高亮构造器放入sourceBuilder中
sourceBuilder.query(termQuery);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
SearchRequest source = searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.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.fragments();
String new_title = "";
for (Text text : fragments) {
new_title += text;
}
sourceAsMap.put("title", new_title); //高亮字段替换掉原来的内容即可
}
list.add(sourceAsMap);
}
return list;
}
コントローラで検索リクエストを変更してみてください
@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 {
List<Map<String, Object>> list = contentService.searchPagehighlighter(keyword, pageNo, pageSize);
return list;
}
アクセスの試み
完了しました