Java爬虫系列(五) - 爬虫补充内容+ElasticSearch展示数据

18 篇文章 0 订阅
9 篇文章 0 订阅

Java爬虫内容补充和ElasticSearch

网页去重

之前我们对下载的url地址进行了去重操作,避免同样的url下载多次。其实不光url需要去重,我们对下载的内容也需要去重。

在网上我们可以找到许多内容相似的文章。但是实际我们只需要其中一个即可,同样的内容没有必要下载多次,那么如何进行去重就需要进行处理了

去重方案介绍

指纹码对比

最常见的去重方案是生成文档的指纹门。例如对一篇文章进行MD5加密生成一个字符串,我们可以认为这是文章的指纹码,再和其他的文章指纹码对比,一致则说明文章重复。

但是这种方式是完全一致则是重复的,如果文章只是多了几个标点符号,那仍旧被认为是不重复的,这种方式并不合理

BloomFilter

这种方式就是我们之前对url进行去重的方式,使用在这里的话,也是对文章进行计算得到一个数,再进行对比,缺点和方法1是一样的,如果只有一点点不一样,也会认为不重复,这种方式不合理。

KMP算法

KMP算法是一种改进的字符串匹配算法。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。能够找到两个文章有哪些是一样的,哪些不一样。

这种方式能够解决前面两个方式的“只要一点不一样就是不重复”的问题。但是它的时空复杂度太高了,不适合大数据量的重复比对。

还有一些其他的去重方式:最长公共子串、后缀数组、字典树、DFA等等,但是这些方式的空复杂度并不适合数据量较大的工业应用场景。我们需要找到一款性能高速度快,能够进行相似度对比的去重方案

Google 的 simhash 算法产生的签名,可以满足上述要求。这个算法并不深奥,比较容易理解。这种算法也是目前Google搜索引擎所目前所使用的网页去重算法。

SimHash

1. 流程介绍

simhash是由 Charikar 在2002年提出来的,为了便于理解尽量不使用数学公式,分为这几步:

1、分词,把需要判断文本分词形成这个文章的特征单词。

2、hash,通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字。

3、加权,通过 2步骤的hash生成结果,需要按照单词的权重形成加权数字串,“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”

“51区”计算为 “ 5 -5 5 -5 5 5”。

4、合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。

“美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”

把每一位进行累加, “4+5 -4±5 -4+5 4±5 -4+5 4+5”à“9 -9 1 -1 1 9”

5、降维,把算出来的 “9 -9 1 -1 1 9”变成 0 1 串,形成最终的simhash签名。

2. 签名距离计算

我们把库里的文本都转换为simhash签名,并转换为long类型存储,空间大大减少。现在我们虽然解决了空间,但是如何计算两个simhash的相似度呢?

我们通过海明距离(Hamming distance)就可以计算出两个simhash到底相似不相似。两个simhash对应二进制(01串)取值不同的数量称为这两个simhash的海明距离。

举例如下: 10101 和 00110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为3。对于二进制字符串的a和b,海明距离为等于在a XOR b运算结果中1的个数(普遍算法)。

3. 导入 simhash 工程

参考项目
这个项目不能直接使用,因为jar包的问题,需要进行改造。这里使用已经改造好的 simhash项目存放在百度云

在idea中导入工程simhash,将其安装到maven仓库即可导入使用, 导入方法参考 我的网站, 导入方法类似

按照测试用例的要求,准备两个文件,就是需要进行对比的文章

在上述安装项目到本地仓库后在之前的案例中导入依赖

<!--simhash网页去重-->
<dependency>
    <groupId>com.lou</groupId>
    <artifactId>simhasher</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

修改代码

@Component
public class TaskTest {

    @Scheduled(cron = "0/5 * * * * *")
    public void test() {
        String str1 = readAllFile("D:/test/testin.txt");
        SimHasher hash1 = new SimHasher(str1);
        //打印simhash签名
        System.out.println("第一个文章的simhash签名" + hash1.getSignature());
        System.out.println("============================");

        String str2 = readAllFile("D:/test/testin2.txt");
        //打印simhash签名
        SimHasher hash2 = new SimHasher(str2);
        System.out.println("第二个文章的simhash签名" + hash2.getSignature());
        System.out.println("============================");
        // 内容如果一样,打印的签名相同

        //打印海明距离(代表相似度), 越小越相似,如果是0说明没有差异
        System.out.println("海明距离: " + hash1.getHammingDistance(hash2.getSignature()));
    }

    /**
     * 测试用
     * @param filename 名字
     * @return
     */
    public static String readAllFile(String filename) {
        String everything = "";
        try {
            FileInputStream inputStream = new FileInputStream(filename);
            everything = IOUtils.toString(inputStream);
            inputStream.close();
        } catch (IOException e) {
        }

        return everything;
    }
}

代理的使用

有些网站不允许爬虫进行数据爬取,因为会加大服务器的压力。其中一种最有效的方式是通过ip+时间进行鉴别,因为正常人不可能短时间开启太多的页面,发起太多的请求。

我们使用的WebMagic可以很方便的设置爬取数据的时间, 但是这样会大大降低我们爬取数据的效率,如果不小心ip被禁了,会让我们无法爬去数据,那么我们就有必要使用代理服务器来爬取数据。

1. 代理服务器

代理(英语:Proxy),也称网络代理,是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。

提供代理服务的电脑系统或其它类型的网络终端称为代理服务器(英文:Proxy Server)。一个完整的代理请求过程为:客户端首先与代理服务器创建连接,接着根据代理服务器所使用的代理协议,请求对目标服务器创建连接、或者获得目标服务器的指定资源。

我们就需要知道代理服务器在哪里(ip和端口号)才可以使用。网上有很多代理服务器的提供商,但是大多是免费的不好用,付费的还行。

提供免费代理ip的服务商网站:

米扑代理

2. 使用代理

WebMagic使用的代理APIProxyProvider。因为相对于Site的“配置”,ProxyProvider定位更多是一个“组件”,所以代理不再从Site设置,而是由HttpClientDownloader设置。

API说明
HttpClientDownloader.setProxyProvider(ProxyProvider proxyProvider)设置代理

ProxyProvider有一个默认实现:SimpleProxyProvider。它是一个基于简单Round-Robin的、没有失败检查的ProxyProvider。可以配置任意个候选代理,每次会按顺序挑选一个代理使用。它适合用在自己搭建的比较稳定的代理的场景。

如果需要根据实际使用情况对代理服务器进行管理(例如校验是否可用,定期清理、添加代理服务器等),只需要自己实现APIProxyProvider即可。

请求能返回地址的api

https://api.myip.com/

为了避免干扰,先把之前项目中的其他任务的@Component注释掉,再在案例中加入编写以下逻辑:

@Component
public class ProxyTest implements PageProcessor {

    @Scheduled(fixedDelay = 1000)
    public void process() {
        // 创建下载器 Downloader
        HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
        // 给下载器设置代理服务器信息
        httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("221.122.91.65", 80)));
        Spider.create(new ProxyTest())
                .addUrl("https://api.myip.com/")
                .setDownloader(httpClientDownloader)// 设置下载器
                .run();
    }

    @Override
    public void process(Page page) {
        System.out.println(page.getHtml().toString());
    }

    private Site site = Site.me();

    @Override
    public Site getSite() {
        return site;
    }
}

2. ElasticSearch环境准备

安装ElasticSearch服务

点击下载需要用到的环境,百度云中

下载 elasticsearch-5.6.8.zip 并解压, 推荐使用1.8及以上64位jdk

进入bin中启动 elasticsearch.bat

当出现以下内容表示启动完成
在这里插入图片描述
访问地址是http://127.0.0.1:9200 访问该地址得到json数据表示ElasticSearch安装启动完成

安装ES的图形化界面插件

安装ElasticSearch的head插件,完成图形化界面的效果,完成索引数据的查看。采用本地安装方式进行head插件的安装。elasticsearch-5-*以上版本安装head需要安装node和grunt。

1)安装head插件

​ 将head压缩包elasticsearch-head-master.zip解压到任意目录,但是要和elasticsearch的安装目录区别开

2)安装nodejs, 直接下一步就行了

3)将grunt安装为全局命令 ,Grunt是基于Node.js的项目构建工具

在cmd控制台中输入如下执行命令:

npm install -g grunt-cli

ps:如果安装不成功或者安装速度慢,可以使用淘宝的镜像进行安装:

npm install -g cnpm –registry=https://registry.npm.taobao.org

后续使用的时候,只需要把npm xxx 换成 cnpm xxx 即可

4)修改elasticsearch配置文件:elasticsearch.yml,增加以下三句命令:

http.cors.enabled: true

http.cors.allow-origin: "*"

network.host: 127.0.0.1

重启

5)进入head目录启动head,在命令提示符下输入命令:

grunt server

在这里插入图片描述
根据提示访问http://localhost:9100,效果如下:
在这里插入图片描述
PS:如果第5步失败,执行以下命令

npm install grunt

再次运次那个grunt server, 根据提示按以下方式依次安装组件

npm install grunt-contrib-clean grunt-contrib-concat grunt-contrib-watch grunt-contrib-connect grunt-contrib-copy grunt-contrib-jasmine

安装成功后再执行 grunt server即可

安装分词器

解压elasticsearch-analysis-ik-5.6.8.zip

将解压后的elasticsearch文件夹拷贝到elasticsearch-5.6.8\plugins下,并重命名文件夹为ik

重新启动ElasticSearch,即可加载IK分词器

1、 测试

在浏览器发起以下请求

ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为 中华人民共和国、人民大会堂。

pretty=true: 美化输出

最小切分, 在浏览器地址栏输入地址

http://127.0.0.1:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序员

浏览器显示

{
  "tokens" : [
    {
      "token" : "我",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "是",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "程序员",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 2
    }
  ]
}

开发环境准备

1, 创建Maven工程并导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.ayulong</groupId>
    <artifactId>ayulong-es</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>5.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>5.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.24</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.4.RELEASE</version>
        </dependency>
    </dependencies>

</project>

2, 编写pojo

@Document(indexName = "item", type = "item")
public class Item {
    //是否用来搜索index = true, 数据类型type = FieldType.Integer
    @Id
    @Field(index = true, store = true,type = FieldType.Integer)
    private Integer id;
    //是否存储store = true
    //analyzer = "ik_smart", searchAnalyzer = "ik_smart",分词方式
    @Field(index = true, store = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", type = FieldType.text)
    private String title;
    @Field(index = true, store = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", type = FieldType.text)
    private String content;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

3, 编写dao

//第一个泛型查询的实体类Item,第二个泛型主键的类型 Integer
public interface ItemRepository extends ElasticsearchRepository<Item, Integer> {
}

4. 编写service和实现

编写service接口

public interface ItemService {

}

编写service实现

@Service
public class ItemServiceImpl implements ItemService {

}

5, 修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/data/elasticsearch
        http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">

    <!--配置Es的Dao扫描-->
    <elasticsearch:repositories base-package="cn.ayulong.es.dao"/>
    <!--配置Es的连接-->
    <elasticsearch:transport-client id="client" cluster-nodes="127.0.0.1:9300"/>
    <!--配置Es模板-->
    <bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        <constructor-arg name="client" ref="client" />
    </bean>
    <!--配置Service扫描-->
    <context:component-scan base-package="cn.ayulong.es.service" />

</beans>

在这里插入图片描述

基本使用

修改service层

修改接口

package cn.ayulong.es.service;

import cn.ayulong.es.pojo.Item;
import org.springframework.data.domain.Page;

import java.util.List;

public interface ItemService {

    /**
     * 新增
     * @param item
     */
    void save(Item item);

    /**
     * 删除
     * @param item
     */
    void delete(Item item);

    /**
     * 批量保存
     * @param list
     */
    void saveAll(List<Item> list);

    /**
     * 查询所有数据
     * @return
     */
    Iterable<Item> findAll();

    /**
     * 分页查询
     * @param page
     * @param rows
     * @return
     */
    Page<Item> findByPage(int page, int rows);
}

修改实现类

@Service
public class ItemServiceImpl implements ItemService {
    //注入dao 直接调用方法
    @Autowired
    private ItemRpository itemRpository;

    public void save(Item item) {
        this.itemRpository.save(item);
    }

    @Override
    public void delete(Item item) {
        this.itemRpository.delete(item);
    }

    @Override
    public void saveAll(List<Item> list) {
        this.itemRpository.saveAll(list);
    }

    @Override
    public Iterable<Item> findAll() {
        Iterable<Item> items = this.itemRpository.findAll();
        return items;
    }

    @Override
    public Page<Item> findByPage(int page, int rows) {
        Page<Item> items = this.itemRpository.findAll(PageRequest.of(page, rows));
        return items;
    }
}

创建测试类进行测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ESTest {

    @Autowired
    private ItemService itemService;
    //ElasticsearchTemplate 模板
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    // 创建索引和映射
    @Test
    public void createIndex() {
        this.elasticsearchTemplate.createIndex(Item.class);
        this.elasticsearchTemplate.putMapping(Item.class);
    }

    // 新增
    @Test
    public void testSave() {
        Item item = new Item();
        item.setId(100);
        item.setTitle("SpringData ES");
        item.setContent("今天我们使用SpringData ES完成搜索功能");

        this.itemService.save(item);
    }

    // 修改, 和保存的代码是一样的, 如果id存在就是修改, 如果id不存在就是新增
    @Test
    public void testUpdate() {
        Item item = new Item();
        item.setId(100);
        item.setTitle("SpringData ES");
        item.setContent("今天我们使用SpringData ES完成job搜索功能");

        this.itemService.save(item);
    }

    // 删除
    @Test
    public void testDelete() {
        Item item = new Item();
        item.setId(100);

        this.itemService.delete(item);
    }

    // 批量保存
    @Test
    public void testSaveAll() {
        // 创建集合
        List<Item> list = new ArrayList<>();

        // 封装数据
        for (int i = 1; i <= 100 ; i++) {
            Item item = new Item();
            item.setId(i);
            item.setTitle("SpringData ES" + i);
            item.setContent("今天我们使用SpringData ES完成job搜索功能" + i);
            list.add(item);
        }

        // 批量保存
        this.itemService.saveAll(list);
    }

    // 查询所有数据
    @Test
    public void testFindAll() {
        Iterable<Item> items = this.itemService.findAll();
        for (Item item : items) {
            System.out.println(item);
        }
    }

    // 分页查询数据
    @Test
    public void testFindByPage() {
        Page<Item> page =  this.itemService.findByPage(1, 30);
        List<Item> items = page.getContent();
        for (Item item : items) {
            System.out.println(item);
        }
    }

}

复杂查询

说明: 复杂查询我遇到了很多问题, 网上也很难找到参考,

查询方法示例
在这里插入图片描述
根据title和Content查询

默认每页显示10条数据

在测试用例中ESTest中添加测试逻辑

/**
 * 根据title和Content查询
 */
@Test
public void findByTitleAndContent() {
    List<Item> list = itemService.findByTitleAndContent("更新", "批量");
    for (Item item : list) {
        System.out.println(item);
    }
}

在ItemService中添加Service接口方法

/**
 * 根据Title和Content查询,交集
 *
 * @param title
 * @param content
 * @return
 */
public List<Item> findByTitleAndContent(String title, String content);

在ItemServiceImpl添加Service实现方法

public List<Item> findByTitleAndContent(String title, String content) {
    List<Item> list = this.itemRepository.findByTitleAndContent(title, content);
    return list;
}

在ItemRepository添加方法

/**
 * 根据Title和Content查询,交集
 *
 * @param title
 * @param content
 * @return
 */
public List<Item> findByTitleAndContent(String title, String content);

根据title和Content分页查询

在测试用例中SpringDataESTest中添加测试逻辑

/**
 * 根据title和Content分页查询
 */
@Test
public void findByTitleOrContent() {
    Page<Item> page = itemService.findByTitleOrContent("版本", "版本", 1, 20);
    for (Item item : page.getContent()) {
        System.out.println(item);
    }
}

在ItemService中添加Service接口方法

 * 根据Title或Content分页查询,并集
 *
 * @param title
 * @param content
 * @param page
 * @param rows
 * @return
 */
public Page<Item> findByTitleOrContent(String title, String content, Integer page, Integer rows);

在ItemServiceImpl添加Service实现方法

public Page<Item> findByTitleOrContent(String title, String content, Integer page, Integer rows) {
    Page<Item> pages = this.itemRepository.findByTitleOrContent(title, content, PageRequest.of(page, rows));
    return pages;
}

在ItemRepository添加方法

/**
 * 根据Title或Content分页查询,并集
 */
public Page<Item> findByTitleOrContent(String title, String content, Pageable pageable);

根据多条件组合查询

在测试用例中SpringDataESTest中添加测试逻辑

/**
 * 根据title和Content和id范围分页查询
 */
@Test
public void findByIdBetween() {
    Page<Item> items = itemService.findByTitleAndContentAndIdBetween("版本", "批量", 31l, 80l, 1, 33);
    for (Item item : items.getContent()) {
        System.out.println(item);
    }
}

在ItemService中添加Service接口方法

/**
 * 根据title和Content和id范围分页查询
 *
 */
public Page<Item> findByTitleAndContentAndIdBetween(String title, String Content, Long min, Long max, Integer page, Integer rows);

在ItemServiceImpl添加Service实现方法

public Page<Item> findByTitleAndContentAndIdBetween(String title, String Content, Long min, Long max, Integer page, Integer rows) {
    Page<Item> items = this.itemRepository.findByTitleAndContentAndIdBetween(title
            , Content, min, max, PageRequest.of(page, rows));
    return items;
}

在ItemRepository添加方法

/**
 * 根据title和Content和id范围分页查询
 *
 */
public Page<Item> findByTitleAndContentAndIdBetween(String title, String Content, Long min, Long max, Pageable pageable);

3. 查询案例实现

说明: 这里虽然我最后运行出来了, 但是还是有integer类型排序和比较依照ascii码的问题, 这里就不放我的代码了, 可能是我的环境有问题, 我等之后有机会再研究一下, 如果运行无数据可以参考我的博客

把上一次上课抓取到的招聘数据作为数据源,实现招聘信息查询功能。首先需要把MySQL的数据添加到索引库中,然后再实现查询功能。我们这里使用的是SpringBoot,需要把Spring Data ElasticSearch 和项目进行整合。

开发准备

我们这里使用的是SpringBoot,需要把Spring Data ElasticSearch 和项目进行整合

需要修改之前的配置,网页去重排除lucene依赖,同时去重的依赖必须放在pom.xml的最下部。因为现在要使用ElasticSearch,需要用到新的lucene依赖。

添加ES依赖和单元测试依赖,并修改以前的去重依赖,pom.xml效果:

<!--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-test</artifactId>
</dependency>

<!--simhash网页去重-->
<dependency>
    <groupId>com.lou</groupId>
    <artifactId>simhasher</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

修改配置文件application.properties,添加以下内容

如果用的是yml则在spring的下一级添加内容, 示例如下

spring:
 #其他代码
 data:
  elasticsearch:
   cluster-nodes: 127.0.0.1:9300

导入数据到索引库
编写ElasticSearch 的pojo

// 这里indexName不小写会报错
@Document(indexName = "jobinfo", type = "jobInfoField")
public class JobInfoField {

    @org.springframework.data.annotation.Id
    @Field(index = true, store = true, type = FieldType.Long)
    private Long id;
    @Field(index = false, store = true, type = FieldType.Text)
    private String companyName; 
    @Field(index = false, store = true, type = FieldType.Text)
    private String companyAddr; 
    @Field(index = false, store = true, type = FieldType.Text)
    private String companyInfo;
    @Field(index = true, store = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", type = FieldType.Text)
    private String jobName;
    @Field(index = true, store = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", type = FieldType.Text)
    private String jobAddr;
    @Field(index = true, store = false, analyzer = "ik_smart", searchAnalyzer = "ik_smart", type = FieldType.Text)
    private String jobInfo;
    @Field(index = true, store = true, type = FieldType.Integer)
    private Integer salaryMin;
    @Field(index = true, store = true, type = FieldType.Integer)
    private Integer salaryMax;
    private String url;
    @Field(index = true, store = true, type = FieldType.Text)
    private String time;
	// 生成 get/set
    // 生成toString()
}

编写dao

@Component
public interface JobRepository extends ElasticsearchRepository<JobInfoField, Long> {
}

编写Service

public interface JobRepositoryService {


    /**
     * 保存一条数据
     *
     * @param jobInfoField
     */
    void save(JobInfoField jobInfoField);

    /**
     * 批量保存数据
     *
     * @param list
     */
    void saveAll(List<JobInfoField> list);
}

编写Service实现类

@Service
public class JobRepositoryServiceImpl implements JobRepositoryService {

    @Autowired
    private JobRepository jobRepository;

    @Override
    public void save(JobInfoField jobInfoField) {
        this.jobRepository.save(jobInfoField);
    }

    @Override
    public void saveAll(List<JobInfoField> list) {
        this.jobRepository.saveAll(list);
    }
}

编写测试用例

先执行createIndex()方法创建索引,再执行jobData()导入数据到索引库

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ElasticSearchTest {

    @Autowired
    private JobInfoService jobInfoService;
    @Autowired
    private JobRepositoryService jobRepositoryService;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 创建索引和映射
     */
    @Test
    public void createIndex() {
        this.elasticsearchTemplate.createIndex(JobInfoField.class);
        this.elasticsearchTemplate.putMapping(JobInfoField.class);
    }


    @Test
    public void jobData() {
        //声明当前页码数
        int count = 0;
        //声明查询数据条数
        int pageSize = 0;

        //循环查询
        do {
            //从MySQL数据库中分页查询数据
            Page<JobInfo> page = this.jobInfoService.findAllPage(count, 500);

            //声明存放索引库数据的容器
            List<JobInfoField> list = new ArrayList<>();

            //遍历查询结果
            for (JobInfo jobInfo : page.getContent()) {
                //创建存放索引库数据的对象
                JobInfoField jobInfoField = new JobInfoField();
                //复制数据
                BeanUtils.copyProperties(jobInfo, jobInfoField);
                //把复制好的数据放到容器中
                list.add(jobInfoField);
            }

            //批量保存数据到索引库中
            this.jobRepositoryService.saveAll(list);

            //页面数加一
            count++;
            //获取查询数据条数
            pageSize = page.getContent().size();

        } while (pageSize == 500);
    }
}

案例实现
页面跳转实现
添加静态资源到项目的resources目录中

编写 pojo

public class JobResult {

    private List<JobInfoField> rows;
    private Integer pageTotal;

	// 生成 get/set 方法
}

编写Controller

@RestController
public class SearchController {

    @Autowired
    private JobRepositoryService jobRepositoryService;

    /**
     * 根据条件分页查询数据
     * @param salary
     * @param jobaddr
     * @param keyword
     * @param page
     * @return
     */
    @RequestMapping(value = "search", method = RequestMethod.POST)
    public JobResult search(String salary, String jobaddr, String keyword, Integer page) {
        JobResult jobResult = this.jobRepositoryService.search(salary, jobaddr, keyword, page);
        return jobResult;
    }
}

编写Service

在JobRepositoryService编写接口方法

@Override
public JobResult search(String salary, String jobaddr, String keyword, Integer page) {
    //薪资处理 20-*
    int salaryMin = 0, salaryMax = 0;
    String[] salays = salary.split("-");
    //获取最小值
    if ("*".equals(salays[0])) {
        salaryMin = 0;
    } else {
        salaryMin = Integer.parseInt(salays[0]) * 10000;
    }

    //获取最大值
    if ("*".equals(salays[1])) {
        salaryMax = 900000000;
    } else {
        salaryMax = Integer.parseInt(salays[1]) * 10000;
    }

    //工作地址如果为空,就设置为*
    if (StringUtils.isBlank(jobaddr)) {
        jobaddr = "*";

    //查询关键词为空,就设置为*
    }   if (StringUtils.isBlank(keyword)) {
        keyword = "*";
    }


    //获取分页,设置每页显示30条数据
    Pageable pageable = PageRequest.of(page - 1, 30);

    //执行查询
    Page<JobInfoField> pages = this.jobRepository
            .findBySalaryMinBetweenAndSalaryMaxBetweenAndJobAddrAndJobNameAndJobInfo(salaryMin,
                    salaryMax, salaryMin, salaryMax, jobaddr, keyword, keyword, pageable);

    //封装结果
    JobResult jobResult = new JobResult();
    jobResult.setRows(pages.getContent());
    jobResult.setPageTotal(pages.getTotalPages());

    return jobResult;
}

编写Dao

在JobRepository编写接口方法

/**
 * 根据条件分页查询数据
 *
 * @param salaryMin1 薪资下限最小值
 * @param salaryMin2 薪资下限最高值
 * @param salaryMax1 薪资上限最小值
 * @param salaryMax2 薪资上限最大值
 * @param jobAddr    工作地点
 * @param jobName    职位名称
 * @param jobInfo    职位信息
 * @param pageable   分页数据
 * @return
 */
public Page<JobInfoField> findBySalaryMinBetweenAndSalaryMaxBetweenAndJobAddrAndJobNameAndJobInfo(Integer salaryMin1, Integer salaryMin2, Integer salaryMax1, Integer salaryMax2, String jobAddr, String jobName, String jobInfo, Pageable pageable);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yzhSWJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值