Hadoop大数据招聘网数据分析综合案例

  1. Hadoop大数据综合案例1-Hadoop2.7.3伪分布式环境搭建
  2. Hadoop大数据综合案例2-HttpClient与Python招聘网数据采集
  3. Hadoop大数据综合案例3-MapReduce数据预处理
  4. Hadoop大数据综合案例4-Hive数据分析
  5. Hadoop大数据综合案例5-SSM可视化基础搭建
  6. Hadoop大数据综合案例6–数据可视化(SpringBoot+ECharts)

Spring Boot 简介

由于Spring是一个轻量级的企业开发框架,主要的功能就是用于整合和管理其他框架。但随着整合的框架越来越多,Spring的整合配置也日益繁琐,一度被人认为“配置地狱”。随着Spring 3.0的发布,Spring IO团队逐渐开始摆脱XML配置文件,并且在开发过程中大量使用约定优先配置的思想来摆脱Spring框架中各类繁复纷杂的配置(即时是Java Config)。
SpringBoot 正是在这样的一个背景下被抽象出来的开发框架,它本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,也不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。

SpringBoot 的核心功能

起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

<!-- 继承默认值为Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
</parent>

应用启动器

名称描述
spring-boot-starter-jdbc使用 JDBCTomcat JDBC 连接池
spring-boot-starter-thymeleaf使用Thymeleaf视图构建MVC Web应用程序的入门者
spring-boot-starter-test支持常规的测试依赖,包括 JUnit 以及 spring-test模块
spring-boot-starter-web支持全栈式 Web 开发,包括 Tomcatspring-webmvc
spring-boot-starter-data-jpa使用 Spring 数据 JPAHibernate 的启动器
mybatis-spring-boot-starter提供了MyBatisSpring整合所需配置及依赖项
druid-spring-boot-starter提供了Druid数据源的整合配置项
mybatis-plus-boot-starterMyBatisPlus框架整合了MyBatisSpring相关依赖和配置项

自动配置

SpringBoot在进行 SpringApplication 对象实例化时会加载 META-INF/spring.factories 文件,根据 pom.xml中加入的坐标,考虑众多因素, 将该配置文件中需要的配置信息载入到 Spring容器,完成自动配置,该过程是 Spring 自动完成的.

SpringBoot 常用注解

注解名称描述
@SpringBootApplication组合注解,包含了@ComponentScan@Configuration@EnableAutoConfiguration注解。
@ConfigurationProperties方法注解,用于获取核心配置文件中对应以什么开头的多个属性,并自动注入到对象中,需要提供 getting ,setting
@SpringBootConfiguration类注解,与@Configuration一样,用于区分SpringBoot配置类和Spring配置类

关闭某一项的自动配置: @SpringBootApplication(exclude={RedisAutoCOnfiguration.class})

快速入门

传统基于Spring的Java Web应用,需要配置Web启动器, SpringIOC容器SpringMVC启动配置,将应用打成war包放入应用服务器Tomcat中并运行。如果基于Spring Boot,这一切都将变得简单:访问Spring官网提供的项目构建页面:Spring Initializr
在这里插入图片描述

把下载后的压缩包解压到项目存放地址,通过IDEA导入到项目工程中,注意一定要选择Maven方式导入。如下图所示:

在这里插入图片描述

导入后,IDEA会进行Maven依赖包下载,要等一会,默认生成的项目架构有很多没用的东西需要删除。如下图所示:

在这里插入图片描述

修改核心配置文件指定数据源信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.43.134:3306/jobdata
spring.datasource.username=root
spring.datasource.password=root

编写MyBatis整合配置类

@SpringBootConfiguration
@MapperScan(basePackages = "com.example.springboot.mapper")
public class MyBatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean;
    }
}

编写entity实体映射类

public class CityCountEntity {
    private String city;
    private int count;

    public CityCountEntity() {}

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "CityCountEntity{" +
                "city='" + city + '\'' +
                ", count=" + count +
                '}';
    }
}

编写MapperSQL操作类

public interface ICityCountMapper {

    @Select("select * from t_city_count ")
    List<CityCountEntity> list();
}

编写Controller控制器类

@RestController
@RequestMapping("/city")
public class CityCountController {

    @Autowired
    private ICityCountMapper cityCountMapper;

    @GetMapping("/list")
    public List<CityCountEntity> list(){
        return cityCountMapper.list();
    }
}

启动SpringBoot应用

在这里插入图片描述

在浏览器中访问测试

在这里插入图片描述

SpringBoot访问静态资源

@SpringBootConfiguration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //第一个方法设置访问路径前缀,第二个方法设置资源路径
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 实现无业务逻辑跳转,减少控制器代码的编写
        registry.addRedirectViewController("/","/index.html");
    }
}

SpringBoot静态资源存放于static目录中,可以通过上述代码进行修改。

在这里插入图片描述

浏览器访问http://localhost:8080/ 会自动重定向到 http://localhost:8080/index.html 页面下,访问效果如下:

在这里插入图片描述

ECharts简介

ECharts ,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),提供直观,交互丰富,可高度个性化定制的数据可视化图表。

  • 数据可视化主要目的:借助于图形化手段,清晰有效地传达与沟通信息。
  • 数据可视化可以把数据从冰冷的数字转换成图形,揭示蕴含在数据中的规律和道理。

入门案例

参考官方文档,入门案例编写一个简单的柱形图,代码如下所示:

<!DOCTYPE html>
<html lang="zh_CN">
<head>
  <meta charset="UTF-8">
  <title>ECharts入门案例</title>
  <script src="lib/echarts.min.js"></script>
</head>
<body>
<!--
  1. 下载并引入 echarts.min.js文件
  2. 准备一个呈现图表的容器,需要有大小
  3. 初始化echarts实例对象
  4. 准备配置项
  5. 将配置项设置给echarts实例对象
-->
  <div id="main" style="width: 600px;height: 400px"></div>
  <script>
    var myECharts = echarts.init(document.querySelector("#main"));
    var option = {
      title: {text: '柱形图示例'},
      tooltip: {},
      legend: {
        data:['销量']
      },
      xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
      },
      yAxis: {},
      series: [{
        name: '销量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]  // y轴数据
      }]
    };
    myECharts.setOption(option);
  </script>
  </body>
</html>

在这里插入图片描述
在这里插入图片描述

除了配置项会变化之外,其它的代码都是固定的。配置项的学习和使用参考官方文档和实例

ECharts 基础概念概览

echarts 实例

一个网页中可以创建多个 echarts 实例。每个 echarts 实例 中可以创建多个图表和坐标系等等(用 option 来描述)。准备一个 DOM 节点(作为 echarts 的渲染容器),就可以在上面创建一个 echarts 实例。每个 echarts 实例独占一个 DOM 节点。

系列(series)

在 echarts 里,系列series)是指:一组数值以及他们映射成的图。一个 系列 包含的要素至少有:一组数值、图表类型(series.type)、以及其他的关于这些数据如何映射成图的参数。
常用的系列类型(series.type):line(折线图)、bar(柱状图)、pie(饼图)、map(地图)、radar(雷达图)、gauge(仪表盘)

组件(component)

在系列之上,echarts 中各种内容,被抽象为“组件”。echarts 中有这些组件:xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、dataZoom(数据区缩放组件)、tooltip(提示框组件)、toolbox(工具栏组件)、series(系列)、…
在这里插入图片描述

使用dataset 管理数据

Apache ECharts 4 开始支持了 dataset 组件用于单独的数据集声明,从而数据可以单独管理,被多个组件复用,并且可以基于数据指定数据到视觉的映射。

option = {
    legend: {},
    tooltip: {},
    dataset: {
        // 用 dimensions 指定了维度的顺序。直角坐标系中,默认把第一个维度映射到 X 轴上,第二个维度映射到 Y 轴上。
        dimensions: ['product', '2015', '2016', '2017'],
        source: [
            {product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
            {product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
            {product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
            {product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
        ]
    },
    xAxis: {type: 'category'},
    yAxis: {},
    series: [
        {type: 'bar'},
        {type: 'bar'},
        {type: 'bar'}
    ]
};

在这里插入图片描述

异步加载数据

var myChart = echarts.init(document.getElementById('main'));
// 显示标题,图例和空的坐标轴
myChart.setOption({
  title: {text: '异步数据加载示例'},
  tooltip: {},
  dataset: {
    dimensions: ['product', '2015', '2016', '2017'],
    source: [
      {product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
      {product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
      {product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
      {product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
    ]
  },
  xAxis: {type: 'category'},
  yAxis: {},
  series: [{type: 'bar'}]
});

// 异步加载数据
$.get('data.json').done(function (data) {
  // 填入数据
  myChart.setOption({
    dataset: {
      source: data
    }
  });
});

招聘数据可视化

后台数据接口编写

编写Mapper查询所有数据

由于招娉数据可视化业务逻辑比较简单,只需要从数据库中查看出来,转换为JSON数据,并提供访问接口即可。而且表结构基本一致,这里就不在单独定义每一个数据库表对象实体,只需要提供一个统一的响应实体即可。

public class ResponseData {

    private String name;
    private int count;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

Mapper操作单一,都是全部查询出即可,这里也不再单独编写Mapper接口,统一在一个Mapper编写。这里需要注意,由于返回实体的属性名和数据库字段名不一致,MyBatis无法成功映射,需要编写ResultMap或使用字段别名进行指定。

public interface ILaGouMapper {
    
    @Select("select city as name,count from t_city_count order by count desc limit 10 ")
    List<ResponseData> listCity();

    @Select("select company as name,count from t_company_count ")
    List<ResponseData> listCompany();

    @Select("select kills as name,count from t_kill_count order by count desc limit 5 ")
    List<ResponseData> listKill();

    @Select("select salary as name,count from t_salary_dist ")
    List<ResponseData> listSalary();

}

编写Controller获取请求和响应JSON数据

@RestController
@RequestMapping("/lagou")
public class LaGouController {

    @Autowired
    private ILaGouMapper laGouMapper;

    @GetMapping("/city")
    public List<ResponseData> listCity(){
        return laGouMapper.listCity();
    }

    @GetMapping("/company")
    public List<ResponseData> listCompany(){
        return laGouMapper.listCompany();
    }

    @GetMapping("/kill")
    public List<ResponseData> listKill(){
        return laGouMapper.listKill();
    }

    @GetMapping("/salary")
    public List<ResponseData> listSalary(){
        return laGouMapper.listSalary();
    }
}

启动Boot服务,使用浏览器进行代码测试

在这里插入图片描述

前端数据图表展示

使用CSS Flex布局

Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持。
FlexFlexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为 Flex 布局。基本语法如下:

.box{
  display: inline-flex;  /*行内元素使用方式*/
  display: flex;  /*块状元素使用方式*/
}

使用方式:就是通过给父盒子添加flex属性,来控制子盒子的位置和排列方式。

属性说明属性值
flex-direction容器内项目的排列方向rowrow-reversecolumncolumn-reverse
flex-wrap容器内项目换行方式nowrapwrap
justify-content项目在水平的主轴的对齐方式flex-startflex-endcenterspace-aroundspace-between
align-items** **项目在垂直的侧轴的对齐方式stretch(子元素未设置高度生效)flex-startflex-endcenter
align-content定义多根轴线的对齐方式,分块布局 多个 flex 容器stretch****、flex-startflex-endcenterspace-betweenspace-around

当前招聘网有4个数据,这里规划四个区域进行数据放置。

<!DOCTYPE html>
<html lang="zh_CN">
<head>
  <meta charset="UTF-8">
  <title>拉勾网招聘数据可视化</title>
  <style>
    #box{
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      align-content: space-between;
      height: 740px;
    }
    #box>div{
      border: 1px gray dashed;
      width: 47%;height: 44%;
      padding: 15px;
    }
  </style>
</head>
<body>
  <div id="box">
    <div class="city">城市岗位需求</div>
    <div class="salary">薪资分布</div>
    <div class="skill">技能要求</div>
    <div class="company">公司福利</div>
  </div>
</body>
</html>

在这里插入图片描述

选择合适的图表模板填充

resources/static/js 目录下加入echarts.jsecharts-wordcloud.jsjquery.min.jsindex.js 文件,用于图表制作、Ajax异步请求发送和自定义JS图表代码。

$(function(){
  // 城市岗位柱形图
  let cityChart = echarts.init(document.querySelector(".city"));
  cityChart.setOption({
    title: {text: '岗位城市分布'},
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [{
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'bar',
      showBackground: true,
      backgroundStyle: {
        color: 'rgba(180, 180, 180, 0.2)'
      }
    }]
  });

  // 薪资分布条形图
  let salaryChart = echarts.init(document.querySelector(".salary"));
  salaryChart.setOption({
    title: {text: '岗位薪资分布'},
    tooltip: {
      trigger: 'axis',
        axisPointer: {
        type: 'shadow'
      }
    },
    grid: {
      left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    xAxis: {
      type: 'value',
        boundaryGap: [0, 0.01]
    },
    yAxis: {
      type: 'category',
        data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
    },
    series: [
      {
        name: '2011年',
        type: 'bar',
        data: [18203, 23489, 29034, 104970, 131744, 630230]
      }
    ]
  });

  // 技能要求数图
  let killChart = echarts.init(document.querySelector(".kill"));
  killChart.setOption({
    title: {text: '岗位技能要求'},
    legend: {
      right: 'right',
    },
    series: [
      {
        name: '面积模式',
        type: 'pie',
        radius: [20, 120],
        center: ['50%', '50%'],
        roseType: 'area',
        itemStyle: {
          borderRadius: 8
        },
        data: [
          {value: 40, name: 'rose 1'},
          {value: 38, name: 'rose 2'},
          {value: 32, name: 'rose 3'},
          {value: 30, name: 'rose 4'},
          {value: 28, name: 'rose 5'},
          {value: 26, name: 'rose 6'},
          {value: 22, name: 'rose 7'},
          {value: 18, name: 'rose 8'}
        ]
      }
    ]
  });

  // 公司福利词云图
  let companyChart = echarts.init(document.querySelector(".company"));
  companyChart.setOption({
    title: {text: '世界人口总量'},
    tooltip: {},
    series: [{
      type: 'wordCloud',
      gridSize: 2,
      sizeRange: [12, 50],
      rotationRange: [-90, 90],
      shape: 'cloud',
      width: '160%',
      height: '120%',
      drawOutOfBound: true,
      textStyle: {
        color: function () {
          return 'rgb(' + [
            Math.round(Math.random() * 255),
            Math.round(Math.random() * 255),
            Math.round(Math.random() * 255)
          ].join(',') + ')';
        }
      },
      emphasis: {
        textStyle: {shadowBlur: 10, shadowColor: '#333'}
      },
      data: [
        {name: 'Sam S Club', value: 10000},
        {name: 'Macys', value: 6181},
        {name: 'Amy Schumer', value: 4386},
        {name: 'Jurassic World', value: 4055},
        {name: 'Charter Communications', value: 2467},
        {name: 'Chick Fil A', value: 2244},
        {name: 'Planet Fitness', value: 1898},
        {name: 'Pitch Perfect', value: 1484},
        {name: 'Express', value: 1112},
        {name: 'Home', value: 965},
        {name: 'Johnny Depp', value: 847},
        {name: 'Lena Dunham', value: 582},
        {name: 'Lewis Hamilton', value: 555},
        {name: 'KXAN', value: 550},
        {name: 'Mary Ellen Mark', value: 462},
        {name: 'Farrah Abraham', value: 366},
        {name: 'NCAA baseball tournament', value: 273},
        {name: 'Rita Ora', value: 360},
        {name: 'Serena Williams', value: 282},
        {name: 'Point Break', value: 265}
      ]
    }]
  });
});

在这里插入图片描述

使用Ajax进行异步请求,替换数据

$(function(){
  // 城市岗位柱形图
  let cityChart = echarts.init(document.querySelector(".city"));
  cityChart.setOption({
    title: {text: '城市岗位需求前10名'},
    tooltip: {
      trigger: 'axis',
      axisPointer: {            // 坐标轴指示器,坐标轴触发有效
        type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
      }
    },
    xAxis: {type: 'category'},
    yAxis: {type: 'value'},
    dataset: {
      // 用 dimensions 指定了维度的顺序。直角坐标系中,默认把第一个维度映射到 X 轴上,第二个维度映射到 Y 轴上。
      dimensions: [],
      source: []
    },
    series: [{
      type: 'bar',
      showBackground: true,
      backgroundStyle: {
        color: 'rgba(180, 180, 180, 0.2)'
      }
    }]
  });
  $.get('/lagou/city').done(function(data){
    cityChart.setOption({
      dataset: {
        dimensions: ['name', 'count'],
        source: data
      }
    });
  });

  // 薪资分布条形图
  let salaryChart = echarts.init(document.querySelector(".salary"));
  salaryChart.setOption({
    title: {text: '岗位薪资分布'},
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'value',
      boundaryGap: [0, 0.01]
    },
    yAxis: {type: 'category'},
    series: [
      {type: 'bar'}
    ]
  });

  $.get('/lagou/salary').done(function(data){
    salaryChart.setOption({
      dataset: {
        dimensions: ['name','count'],
        source: data
      },
    });
  });

  // 技能要求数图
  let killChart = echarts.init(document.querySelector(".kill"));
  killChart.setOption({
    title: {text: '岗位技能要求前5名'},
    tooltip: {
      trigger: 'item',
      formatter: '{b} : {d}%'
    },
    legend: {
      top: 'bottom'
    },
    series: [
      {
        type: 'pie',
        radius: [20, 120],
        center: ['50%', '50%'],
        roseType: 'area',
        itemStyle: {
          borderRadius: 8
        }
      }
    ]
  });

  $.get('/lagou/kill').done(function(data){
    killChart.setOption({
      dataset: {
        dimensions: ['count','name'],
        source: data
      }
    });
  });

  // 公司福利词云图
  let companyChart = echarts.init(document.querySelector(".company"));
  companyChart.setOption({
    title: {text: '公司福利待遇情况'},
    tooltip: {},
    series: [{
      type: 'wordCloud',
      gridSize: 2,
      sizeRange: [12, 50],
      rotationRange: [-90, 90],
      shape: 'cloud',
      width: '160%',
      height: '120%',
      drawOutOfBound: true,
      textStyle: {
        color: function () {
          return 'rgb(' + [
            Math.round(Math.random() * 255),
            Math.round(Math.random() * 255),
            Math.round(Math.random() * 255)
          ].join(',') + ')';
        }
      },
      emphasis: {
        textStyle: {shadowBlur: 10, shadowColor: '#333'}
      },
      data: []
    }]
  });

  $.get('/lagou/company').done(function(data){
    data = data.map(item=>{return {'name':item.name,'value':Number.parseInt(item.count*1)}});
    companyChart.setOption({
      series: [{
        data: data
      }]
    });
  });
});

最终效果图展示

在这里插入图片描述

到此,Hadoop大数据综合案例讲解完毕

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐