在公司做小店铺的电商项目的时候,自己虽然大部分任务是放在前端,但是也在群里听到他们说清ES的数据,我就在想ES是个什么玩意,然后便去搜索看看。发现ES也就是ElasticSearch。
ElasticSearch是基于 Lucene的搜索服务器,它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful接口。RESTful指的是具有REST风格架构风格的。对于REST:分布式应用架构风格,其实我自己也没有特别理解。在网上看到的说:面向资源是REST最明显的特征,比如HTTP协议就是属于REST架构的设计模式:无状态请求。这里给一个链接可以参考一下这篇文章:
Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。这里说的实时搜索值的是近实时搜索。同时在网络上看到ElasticSearch跟solr进行比较(两种搜索引擎我都没有了解过,这几天才开始尝试)
我这边安转ElasticSearch在Windows跟Linux系统上面都进行过尝试,Windows安装过程挺顺利的,但是Linux安装非常坎坷,昨天折腾一下午,最后还是安转好了(因为是腾讯的学生云主机)内存比较小,所以需要改虚拟机的启动配置不然总是被killed,然后其他的问题遇到就百度,没有进行记录,这里不再赘述了。
下面结合Spring Boot开始使用ElasticSearch。
资源文件:
spring.data.elasticsearch.cluster-name=my-application //这里是你配置文件中设置的名字 spring.data.elasticsearch.cluster-nodes=111.***.**.***:9300 //服务器IP spring.data.elasticsearch.repositories.enable=true server.port=8081
配置好资源文件之后,就开始使用它了(我这里是要爬取我自己CSDN博客的个人信息去存储到ElasticSearch,并获取)
关于爬取信息在文章末尾也叙述一下,这里还是来说一下Java操作ElasticSearch的过程
首先是Dao层:
package com.elastic.search.dao; import com.elastic.search.vo.CSDNInfoVO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Component; @Component public interface CsdnDao extends ElasticsearchRepository<CSDNInfoVO, Long> { CSDNInfoVO queryById(Long id); }@Component:
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
关于ElasticsearchRepository这个类看后面这个单词Repository,可以猜测他是一个数据访问的接口
然后是Service层
public List<CSDNInfoVO> getcsdninfo() { List<CSDNInfoVO> list = new ArrayList<>(); Long num = csdnDao.count(); for (long i = 1; i <= num; i++) { list.add(csdnDao.queryById(i)); } return list; }
这里其实是我自己定时爬虫爬取的数据,并且只有10条,代码如下
@Scheduled(cron = "0 0 0,13,18,21 * * ?") public void testCron() { try { CSDNInfoVO csdnInfoVO = csdnHtml.getInfo(); Long num = csdnDao.count(); if (num > 9) { //一旦ES中的数据量超过10条,就开始清除第一条数据,保证ES数据为10条不会变 CSDNInfoVO csdnInfoVO2 = csdnDao.queryById(Long.parseLong("1")); csdnDao.delete(csdnInfoVO2); for (long i = 2; i < 11; i++) { csdnInfoVO2 = csdnDao.queryById(i); csdnInfoVO2.setId(i - 1); csdnDao.save(csdnInfoVO2); } num = new Long((long) 9); } csdnInfoVO.setId(num + 1); csdnInfoVO.setCreateTime(new Date()); csdnDao.save(csdnInfoVO); } catch (KeyStoreException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } logger.info("===initialDelay: 第{}次执行方法", csdnDao.count()); } }
控制层(这个是我测试使用的,真正的控制层现在只有一个方法)
@RequestMapping("add") public String add(){ TestVO testVO = new TestVO(); testVO.setId("2"); testVO.setName("陈龙"); testDao.save(testVO); return "success"; } @RequestMapping("query") public TestVO query(){ TestVO testVO = testDao.queryTestById("1"); return testVO; } //删除 @RequestMapping("/delete") public String delete(){ TestVO testVO = testDao.queryTestById("1"); testDao.delete(testVO); return "success"; } //局部更新 @RequestMapping("/update") public String update(){ TestVO employee=testDao.queryTestById("1"); employee.setName("Elastic Search Test 陈龙"); testDao.save(employee); System.err.println("update a obj"); return "success"; }
vo
@Document(indexName = "csdn",type = "info",shards = 1,replicas = 0,refreshInterval = "-1") //这里indexName类似于数据库名注意要小写 public class CSDNInfoVO { //type 类似于表名 下面就是字段了 @Id private Long id; @Field public String csdnName; @Field public String csdnImg; @Field public String csdnArticl; @Field public String csdnFenSi; @Field public String csdnLike; @Field public String csdnComment; @Field public String csdnGrade; @Field public String csdnVisitNum; @Field public String csdnIntegral; @Field public String csdnRanking; @Field public Date createTime; //set get 方法这里不在复制}
ok,这就是Spring Boot简单结合ElasticSearch。
下面来记录一下爬虫的过程:
首先自己使用的是HTMLunit这个框架,但是它对元素的获取我觉得有点麻烦,于是最后又结合自己熟悉的Jsoup来完成数据的爬取:
package com.elastic.search.htmlunit; import com.elastic.search.vo.CSDNInfoVO; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.*; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.IOException; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @Service public class CSDNHtml { private Logger logger = LoggerFactory.getLogger(CSDNHtml.class); public CSDNInfoVO getInfo() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException, IOException { CSDNInfoVO csdnInfoVO = new CSDNInfoVO(); WebClient webClient = WebClientFactory.INSTANCE.getWebClient(); HtmlPage htmlPage = WebClientFactory.INSTANCE.getHtmlPage(); webClient.getOptions().setJavaScriptEnabled(false); webClient.getOptions().setCssEnabled(false); webClient.getOptions().setUseInsecureSSL(true); // SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { // public boolean isTrusted(X509Certificate[] chain, // String authType) throws CertificateException { // return true; // } // }).build(); // webClient.waitForBackgroundJavaScript(30000); HtmlDivision domElement = htmlPage.getHtmlElementById("asideProfile"); String pageXml = domElement.asXml(); Document document = Jsoup.parse(pageXml); // System.out.println(document.body()); Elements elements = document.select("img[src]"); for (Element element : elements) { csdnInfoVO.setCsdnImg(element.attr("src")); } String name = document.getElementById("uid").text(); Elements elements1 = document.getElementsByTag("dd"); Elements elements2 = document.getElementsByTag("dl"); String article = elements1.get(0).text(); String fensi = elements1.get(1).text(); String like = elements1.get(2).text(); String comment = elements1.get(3).text(); String grade = elements1.get(4).getElementsByTag("a").attr("title"); String visitNum = elements1.get(5).attr("title"); // System.out.println(elements1.get(6).attr("title")); String csdnIntegral = elements1.get(6).text(); String rank = elements2.get(7).attr("title"); csdnInfoVO.setCsdnName(name); csdnInfoVO.setCsdnArticl(article); csdnInfoVO.setCsdnFenSi(fensi); csdnInfoVO.setCsdnLike(like); csdnInfoVO.setCsdnComment(comment); csdnInfoVO.setCsdnGrade(grade); csdnInfoVO.setCsdnVisitNum(visitNum); csdnInfoVO.setCsdnIntegral(csdnIntegral); csdnInfoVO.setCsdnRanking(rank); logger.info(csdnInfoVO.toString()); return csdnInfoVO; } public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { CSDNHtml csdnHtml = new CSDNHtml(); // for (int i=0;i<4;i++){ csdnHtml.getInfo().toString(); // } } }
最开始的使用我在方法里面new webClient跟HTMlPage这个对象,但是我想到每次创建这个实例花销太大(自己看到控制台消息跑的飞起,就知道这个实例创建比较费劲),于是就想到单例模式,说到单例模式这里不得不说一下Effective Java这本书,这本书上说推荐使用枚举类型来实现单例模式,在以前我见到的单例模式--懒汉,饿汉,以及网上看到的双重校验锁
public static Singleton getSingleton() { if (instance == null) { //Single Checked synchronized (Singleton.class) { if (instance == null) { //Double Checked instance = new Singleton(); } } } return instance ; }
也就是对象加锁,由于使用了单例模式,所以后续的对象创建次数会比较小,在加锁之前进行一次判断。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。
我这里使用的是枚举创建单例模式
package com.elastic.search.htmlunit; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlPage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; public enum WebClientFactory { INSTANCE; private Logger logger = LoggerFactory.getLogger(WebClientFactory.class); private WebClient webClient=null; private HtmlPage htmlPage=null; WebClientFactory() { logger.info("示例创建"); webClient = new WebClient(); try { htmlPage = webClient.getPage("https://blog.csdn.net/qq442270636"); } catch (IOException e) { e.printStackTrace(); } logger.info("示例创建結束"); } public HtmlPage getHtmlPage() { return htmlPage; } public WebClient getWebClient() { return webClient; } }
走到这里其实已经走完了。看一下效果