大数据专栏一-全自动化在线式当当销售情况分析预测系统

项目题目

  Hadoop综合应用——基于HDFS、MapReduce、HBase和XGBoost和Echarts和爬虫的全自动化在线式当当销售情况分析预测系统

项目描述

  企业级的web系统要实现全自动化操作,所以我在本次大作业中完成了一个基于HDFS、MapReduce、HBase和XGBoost算法和Echarts和Python爬虫全自动化在线式的当当图书销售情况分析预测系统,可以用来帮助当当更改地分析图书销售情况,改进图书的推荐算法,在这个系统中后端我整合集成了Hadoop框架和Python爬虫和机器学习算法XGBoost和SpringBoot框架,实现了数据采集清洗分析预测过程的全流程自动化处理,不需要自己进行任何操作。我使用SpringBoot和Vue开发的一个前后端分离的全自动在线式的当当图书销售情况分析预测分析平台,该分析预测平台提供底层数据支持的是大数据技术包括HDFS、MapReduce、HBase等大数据框架Hadoop的生态组件。
  鉴于Hadoop框架里的用来提供机器学习算法的Mahout开源库中并不支持XGBoost算法等类似的集成算法,所以我在该Java项目中集成了Python代码用来实现XGBoost算法来完成图书销售情况的动态预测的功能。在预测前我利用了Java来实现数据清洗的功能,当然因为我在爬取数据时候使用了正则表达式进行模式匹配,所以字符串的格式已经大部分正常,不需要很多数据处理。
  由于实际上我们在本地爬取的原始数据的数据量都是十分有限的,远远达不到企业级应用的TB级别的数据量,Hadoop本身具有的高延迟等特性在我们构建的案例场景下其实表现的并不明显。所以我在本Java项目中集成了Python动态爬取的爬虫代码,模仿Storm框架实时流处理的案例场景尝试了建立Hadoop框架在线式的数据分析预测的项目架构用来处理来自于原始网页的实时流数据,从而实现该系统的全自动在线式分析预测数据的功能。
  我们知道Hive数据仓库工具的作用是将结构化的数据文件映射为一张数据库表,并提供SQL查询功能,能将SQL语句转变成MapReduce任务来执行。通过类SQL语句实现快速MapReduce统计,使MapReduce编程变得更加简单易行。Hive组件本身只是在Hadoop框架的MapReduce任务计算引擎上的封装 ,应用场景自然更局限,不可能满足所有需求。Hive查询操作过程严格遵守Hadoop MapReduce的作业执行模型,Hive将用户的HiveQL语句通过解释器转换为MapReduce作业提交到Hadoop集群上,Hadoop监控作业执行过程,然后返回作业执行结果给用户。相对来讲Hive使用的是封装好的MapReduce任务,在有些案例场景下计算效率将会过于低下。除此之外,如果直接使用Hive数据仓库,底层MapReduce任务都是已经封装好的,我们就无法进一步加深对于MapReduce任务的认识。鉴于以上原因,我在本次在线式当当图书销售情况分析预测系统中没有采用Hive数据仓库而是采用了MapReduce+HDFS+HBase的组合方式来完成动态查询的任务,HDFS作为Hive格式化数据的数据源,MapReduce作为实现Hive查询语句的底层MapReduce任务的代替,HBase作为存放Hive查询结果表的数据库,通过这种方式来模拟Hive数据仓库的查询功能。
  针对项目来说,数据安全性是最重要,万一系统遇到错误非正常关闭可能会导致数据文件缺失或者错误。所以我尝试自己实现了项目安全性备份机制。针对HDFS集群,本地文件,HBase集群上三个地方的数据文件我保证了他们具有一致性,并且有地方的数据发生缺失时项目不会发生任何错误,它能自动从其他两个地方备份数据。例如某一时刻由于断电之类的原因导致本地文件缺失时工程能从项目之前上传到云端的数据中自动下载离当前时间最近版本的数据文件来更新本地的数据文件,并不需要任何人为操作。
  整个项目的数据流:在SpringBoot工程里启动的定时任务的第一步就是自动从网络原始网址爬取12份数据,爬取完以后接着根据这部份数据执行XGBoost算法的代码,构建预测模型,等待导出结果,然后将结果上传到HBase集群的结果表里。对于爬取下来的12份数据,一方面立刻上传到HDFS集群当作备份的数据文件,另一方面在java代码里面进行数据清洗转换格式,然后再上传到HDFS集群上作为MapReduce任务的数据源。然后再执行的是MapReduce任务,将执行后的结果传到HBase集群的表里。
  下面我作了整个项目的数据流图用来观察在整个Hadoop项目中数据流的变化。

# 数据集说明

开发环境

  操作系统:Windows 10
  虚拟机环境:VMware Workstation 16.1.2
  Linux系统:Ubuntu 20.04.2.0
  开发工具:IntelliJ IDEA Ultimate 2021.1.0.0
  ZooKeeper版本:3.6.3
  HBase版本:2.3.6
  Hadoop版本:3.2.1
  SpringBoot版本:2.5.2
  vue/cli版本:4.5.13
  Web服务器:SpringBoot自带tomcat。
  浏览器:火狐浏览器
  Python:3.7.9
  科学库:Numpy 1.19.2
  Pandas 1.3.4
  Xgboost 1.4.2

开发步骤

需求分析与系统设计

  当当网在线式图书销量分析预测系统是一个依托Hadoop大数据框架进行智能化处理的系统。该系统要求能够对当当网畅销图书排行榜上的数据,包括2021年过去的月份中畅销500本书籍的数据信息和最近7天、最近24小时、最近30天的畅销500本书籍的数据信息进行全自动化在线式信息采集,即不需要人为操作爬虫代码进行爬取数据的操作,在爬取完数据以后保存到本地存储,并自动按照上传文件的时间对文件命名的方式把所有时间接受到的爬取的文件数据上传到HDFS集群作为项目数据的备份,以此来实现项目安全性备份机制,防止数据丢失。在项目的代码跑起来以后能够实时地更新HBase表格上的数据,在前端展示层可以实时地根据数据的变化而发生变化,在前端展示的表格类型的数据或者使用Echarts进行数据可视化展现的图表也能实时地跟随着变化。对于MapReduce任务,我在本地使用Java对xls格式的数据文件进行了数据清洗,然后将其转化为便于MapReduce计算任务的txt格式的数据文件并且将其上传到了Hadoop集群的HDFS上。作为MapReduce任务的格式化数据源,并且执行MapReduce任务将其统计结果存到HBase表格上,并且将其实时地在前端界面展现出来。针对于预测任务我在执行完数据爬取后立刻就开始调用另外的XGBoost算法的代码,并且将XGBoost代码生成的结果格式的xls类型的文件通过Java代码读取后上传到HBase表格上。针对前端,我除了在表格里展现历史数据,和经过MapReduce计算任务得到的统计结果,还需要使用Echarts对结果进行一些可视化展现。
  MapReduce计算任务如下:
  1、统计每个月每家出版商出版的图书数量。
  2、查找每个月推荐指数是100%的图书。
  3、查找每个月评论次数超过100000的图书。
  4、统计每个月每家出版商出版的推荐指数大于95%的图书数量。
  5、查找每个月评论次数超过100000而且价格低于20的图书。
  6、统计每个月每本图书的评论次数与推荐指数。
  7、统计每个月每家出版商出版的图书的评论次数的总和。
  对于本系统的设计,在前端部份,肯定是使用Vue+ElementUI+Echarts组合的方式来实现以图片的形式进行数据可视化,绘制出的图片的数据来源和统计结果表格里的数据来源一致,都是相同的数据来源。对于后端SpringBoot框架部份,其要实现两个核心功能,一是和前端的Vue框架有关,能够以MVVM的架构方式与前端网页内容和后端HBase底层数据进行数据交互,这是实时性的。二是定时任务,为了实现定时任务我们可以使用@scheduled注解,在该组件的帮助下我们能够自动爬取数据,自动进行数据清洗,自动备份数据,当本地文件缺失时,自动更新本地文件,自动上传到HDFS的集群上,自动执行MapRuce任务,自动执行XGBoost算法。

Python环境搭建

  在Java工程里面想要调用Python脚本文件时,应该本机上要装有Python的环境。如果通过cmd命名提示符输入python能够切换进入本机的python编程环境,该环境上可以直接执行python命令,不是经过封装后的python编译环境。通常我们使用Anaconda进行编写python的时候,使用的python编译环境是Anaconda集成自带的,而不是本机系统的。所以我们在开发项目之前,需要首先配置本机系统的Python环境。成功配置系统Python环境后的结果如下图所展现的。

后端整体架构设计

  通过前面的分析我们知道对于后端SpringBoot框架部份,它承担了很多的任务,但是总结来说这个SpringBoot框架其要实现两个核心功能,一是将存放在Hadoop框架上例如HBase数据库的底层数据通过MVVM的前后端交互的程序架构与前端框架Vue进行数据交互,并且在前端页面上展现出来底层HBase数据库的数据进行数据交互,这是实时性的。二是我们将数据采集分析预测的功能通过以定时任务的方式实现对来自于网页原始数据的实时流进行处理。
  为了实现定时任务我们可以使用@scheduled注解,在该组件的帮助下我们能够自动爬取数据,自动进行数据清洗,自动备份数据,当本地文件缺失时,自动更新本地文件,自动上传到HDFS的集群上,自动执行MapRuce任务,自动执行XGBoost算法。
  下面我们使用IDEA提供的Diagrams插件对项目的类图进行分析,生成的类图如下所示。
在这里插入图片描述

Python爬虫爬取数据

  首先我们分析先要分析网页结构,我们首先以2021年1月份推荐的图书为例。前往当当网图书页面,http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-month-2021-1-1-1。经过分析可以发现一页20本图书,且不同页数在http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-month-2021-1-1-1中的-1-1-1发生变化。当当2021年1月的畅销图书排行的第二页是http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-month-2021-1-1-2。最后几个数字决定了页数。接着我们观察当当2021年1月的畅销图书网页的内容,即http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-month-2021-1-1-1的html的结构特征。
在这里插入图片描述
  通过网页的html特征我们可以很容易地发现bang_list是储存的图书商品列表。接下来是编写代码,代码部份的思路如下所示。
  1.使用page进行翻页操作。
  2.使用requests请求当当网。
  3.使用正则表达式将返回的HTML源码进行解析。
  4.使用xlwt将解析完的数据使用保存到xlwt格式的文件中,便于后续上传到HBase数据库里。
  使用正则表达式将请求到的一串字符串分离成对应的数据的代码。

pattern = re.compile('<li>.*?list_num.*?(\d+).</div>.*?<img src="(.*?)".*?class="name".*?title="(.*?)">.*?class="star">.*?target="_blank">(.*?)条评论.*?class="tuijian">(.*?)%推荐</span>.*?class="publisher_info">.*?target="_blank">(.*?)</a>.*?class="publisher_info">.*?target="_blank">(.*?)</a>.*?<span\sclass="price_n">&yen;(.*?)</span>',re.S)

软件测试方法-基于功能性的梯度测试

  实际上我们的对于Python爬虫代码的要求是能够在Java的SpringBoot工程项目里通过操作系统命令调用执行Python环境爬虫部份的代码,并且把爬取结果成功导出到项目本地。
  我们要怎么样在项目还没搭建完成的时候,即Python代码还没有集成到SpringBoot框架的情况下进行测试来检验这部分代码是否出现bug呢。我对于如何检验Python爬虫代码的正确性这个问题,使用了三种方式。第一种方式是直接在PyCharm等Python的编译器上直接运行代码。如果在PyCharm等Python的编译器上直接运行代码没有遇到问题,并且能正常地导出数据,说明该部分爬虫代码并不存在任何逻辑错误。在这种方式下进行的测试对应的是Python爬虫代码能够在Python编译器里正确运行。第二种方式是打开cmd命令行。使用python命令直接进入该系统安装的Python环境,在该交互式命令行直接输入Python的爬虫文件中的内容。我们可以发现通过交互式的命令行程序测试爬虫代码时,爬虫代码的运行结果是正常的,而且也能正常导出结果文件。说明该系统安装的Python环境能够正常运行该爬虫代码。这种方式下进行的测试对应的是Python爬虫代码能够在操作系统的Python环境里正确运行。最后一种测试方式是使用子进程的方法,通过在Java项目的父进程中创建子进程process,并且调用Runtime.getRuntime()方法,给这个子进程开辟一段新的虚拟内存空间,让Python脚本文件在该虚拟的内执行。通过这种方法我们能够知道,我们能否在Java项目中使用子进程调用操作系统的Python环境执行我们的Python脚本程序。

Process processt = Runtime.getRuntime().exec(url);

前后端环境搭建

  首先我们在搭建SpringBoot项目时,要注意配置后端网络的端口号,通过在application.properties指定server.port为8080,能让前端Vue项目在访问8080这个端口号,能够找到这个隐藏在这个端口号后面的后端项目。
在这里插入图片描述
  其次我们在搭建Vue项目时,要注意配置前端网络的端口号和允许访问后端的跨域访问配置,通过指定host和port能够指定前端项目端口号为8081,主机名为localhost。跨域访问访问的ip位置是http://localhost:8080,通过正则表达式能够匹配掉api以前的多余的字符。
在这里插入图片描述
  在前端想要开发Echarts进行数据可视化时需要安装该包。通过在项目路径下直接使用npm进行包的安装,既能在项目中成功使用,也能避免这个新导入的包对其他npm环境造成影响。

npm install echarts --save

  在后端项目SpringBoot项目中,由于我们开发项目时,需要针对HBase,HDFS等各种不同的Hadoop生态组件开发相关的代码。所以我们需要导入相关的Maven依赖。
  针对Hadoop的Maven依赖:

<dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.2.1</version>
 </dependency>
 <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-yarn-api</artifactId>
            <version>3.2.1</version>
</dependency>
<dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>3.2.1</version>
</dependency>
<dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>3.2.1</version>
</dependency>

  针对HBase的Maven依赖:

<dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.3.6</version>
</dependency>
<dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.3.4</version>
</dependency>
<dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-common</artifactId>
            <version>2.3.6</version>
</dependency>

  针对MapReduce的Maven依赖:

<dependency>
            <groupId>org.apache.hadoop</groupId>       
            <artifactId>hadoop-mapreduce-client-common</artifactId>
            <version>3.2.1</version>
</dependency>
<dependency>
            <groupId>org.apache.hadoop</groupId>            
            <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
            <version>3.2.1</version></dependency>
<dependency>
            <groupId>org.apache.hadoop</groupId>           
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>3.2.1</version>
</dependency>

  针对SpringBoot框架的Maven依赖

<dependency>
            <groupId>org.springframework.boot</groupId>               
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

  由Maven工程导入过多的Maven依赖,可能会产生了一些严重的异常错误,所以我们可以使用Maven工程给我们提供的依赖冲突排除工具Dependency Analyzer去排除冲突的Maven依赖。
在这里插入图片描述

后端定时任务

  SpringBoot框架中使用@EnableScheduling注解,能让Spring容器把有这个注解的类看成定时任务的启动类定时执行任务。在该启动类内部的方法上使用@Scheduled注解就会让Spring容器每隔一段时间定时执行该方法,其中fixedRate指定的是间隔时间,1000*450代表,每隔450秒就执行一次该方法。对于logcollection,是定时任务的扩展接口,将要执行的定时任务的对应的dao方法集成到该接口中,并且设定接口扩展TimerTask接口,使得它成为定时任务,没隔之前设定的时间单位后,即450秒后,该扩展接口就将该接口内的所有对应的dao方法执行一遍。
在这里插入图片描述

后端集成Python代码

  在Java中如果需要调用第三方程序,可以直接通过Runtime实现,这也是最直接最粗暴的做法,粒度更加粗糙,效率较高,需要安装Python软件。而Jython是Python语言在Java平台的实现,可以理解为一个由 Java 语言编写的 Python 解释器,因此,不需要安装Python软件。它不仅提供了Python的库,同时也提供了所有的Java类,这就使得其有一个巨大的资源库。Jython可以直接调用Python程序中的指定函数或者对象方法,粒度更加精细。但遗憾的是,Jython运行速度并不理想。
  鉴于Jython的资源库没有完整实现Python里的所有库,例如Python里对于xls表格数据操纵的工具库xlwt没有在Jython库里完整实现,同时在本案例场景中我们需要使用sklearn科学库和XGBoost机器学习的算法库,而这两个库在Jython资源库里面也没有完整实现。鉴于以上的原因我在本次项目中我使用了Runtime的方法,从而实现在Java工程项目里使用Python语言进行机器学习和科学计算。
  使用Runtime方法在Java项目里面执行Python语言首先需要我们创建process进程类的对象。在实际Java开发工作中可能会遇到调用操作系统命令的场景,比如查看下文件夹,执行下sh/exe文件等等。在本案例中就是切换调用操作系统的Python编译环境。

Process process = Runtime.getRuntime().exec(“python D:\\a.py”);

在这里插入图片描述
  Process是个抽象类,继承自Object,有两种方式可以创建Process子类的实例,以及一系列进程交互方法。Process提供了WaitFor和getInputStream两个方法,这两个方法都是阻塞java线程,等待脚本返回或结束后,再继续执行java程序。但是创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程或者shell 脚本等等。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、 stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。
  这个问题是进程代码编写的时候最需要注意的问题。如果我们在子进程的输出流里既有print的字符输出流,还同时输出xls、txt等多种格式的文件输出流,这就会导致子进程发生进程阻塞的问题,这个时候从父进程的角度来看,代码就一直停留在子进程,不再执行。这个问题不好观测,当发生问题时也不好确定是不是Python代码内部出问题。通过我在改变Python代码内部文件输出内容后,即修改输出流的数量以后,我就能在项目路径下观测出产生了输出文件。在Java里面调用Python代码如下所示。注意在子进程执行结束后要销毁子进程,即Process.destory()。
在这里插入图片描述

后端HDFS存储历史数据功能

  针对web项目来说,最重要的是保证数据的安全。当出现网络异常、系统服务器异常重启、等问题时数据要求不能突然丢失。这对于我们要求要能够即时备份存储历史数据。我们可以通过使用连接HDFS客户端的API实现数据文件的定时备份上传到HDFS集群的功能,上传的时候我们对文件按照系统当前时间进行命名,方便后面的项目安全性备份机制运作。
在这里插入图片描述
  获取项目路径,我使用的是 System.getProperty(“user.dir”),通过这个命令能够获得IDEA工程的项目路径。获得系统时间使用的是Timestamp(System.currentTimeMillis())命令。将本地数据文件上传到HDFS集群使用的是fs.copyFromLocalFile命令,后面两个参数一个是本地的数据文件路径,一个是HDFS端文件的路径。对上传的文件我是按照数据源名字_系统当前时间的形式在HDFS集群上命名的。

String url2 = System.getProperty("user.dir");
String url3 = "\\book"+String.valueOf(g)+".xls";
Timestamp timestamp = new; Timestamp(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String datatime = sdf.format(timestamp);
String way = url2 + url3;
System.out.println(way);
String wayja = "\\dataanalyse\\crawler\\totalbook\\book\\" + String.valueOf(g) + "_" + datatime + ".xls";
System.out.println(wayja);
fs.copyFromLocalFile(false, true, new Path(way),
new Path(wayja));

后端项目安全性备份机制

  如果项目系统本地出现网络异常、系统服务器异常重启等问题时,正常情况下都会导致项目保存的数据出现了明显的问题。在整个系统设计中,本地数据文件承担了数据交互的重大任务,项目本地的数据文件同时会与HDFS集群上的数据和HBase上的数据进行交互。一旦本地文件出现数据丢失的问题将会导致HDFS集群和HBase上存放的数据出现了明显的问题。
  由于在整个数据传输过程,我是遵循先HDFS再HBase再HDFS再HBas的过程,所以我对于这个问题的处理流程如下所示。
  1、在Java里执行Python脚本获取到网页数据后,下载保存网页数据到项目本地路径。
  2、根据项目本地路径下的本地数据构建训练集和测试集,执行XGBoost任务。
  3、对于下载到本地的网页数据,在上传到HDFS集群之前,先遍历本地的数据文件,检测其是否发生了文件缺失。
  4、如果文件不存在缺失的情况,则正常将该数据文件上传到HDFS集群。
  5、如果文件存在缺失的情况,则马上遍历HDFS集群上保存的该月份对应的文件夹里面对应的数据文件。
  6、在遍历数据文件时截取对应的时间部份的字符串,并且将其转成double类型的数值型数据。
  7、由于aaaabbccddeeff类型的年月日小时分钟秒钟类型的时间类型数据,可以重新划分成ABCDEF6段类型的时间数据。对于两个时间类型的数据x、y,如果时间x比时间y的大的话,6段类型的数据高段位的肯定更大,所以对于数据本身也肯定更大。所以遍历找到数值型数据最大的那个文件,那个文件就是离当前时间最近的那一版本的数据文件。
  8、将该数据文件下载到本地。
  9、将本地文件上传到HDFS集群上从而实现文件时间的更新。
在这里插入图片描述

后端HBase更新实时数据功能

  由于HBase是分布式运行的,单个节点处理数据的能力有限,不能胜任大数据处理问题。所以我们要对JavaAPI操作进行一定的性能优化。
  首先是rowKey的设计,rowKey设计的好与坏将非常明显地影响数据库的查询效率。由于rowKey是按字典序存储的,所以要将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块产能提高数据库查询和读写的效率。使用Long.MAX_VALUE – timestamp作为rowKey,这样能保证新写入的数据在读取时可以被快速命中。
在这里插入图片描述  其次是和数据库连接connect对象的获取。由于connect是重量级的对象,table等才是轻量级的对象。table对象的创建不需要消耗太多资源,而connect对象的创建需要消耗很多资源,所以connect对象应该定义为全局静态变量,程序运行时就创建,所有地方都共用该连接。
在这里插入图片描述
  最后是创建表的时候,可以通过HColumnDescriptor.setInMemory(true)将表放到RegionServer的缓存中,保证在读取的时候被cache命中。通过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的最大版本,可以控制数据备份的份数。还以通过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)。
在这里插入图片描述

后端Java数据清洗功能

  正常来讲是可以在Python代码里面实现数据清洗的功能。但是我在项目中并不打算使用Python进行数据清洗,而是使用了Java语言实现数据清洗的功能。在本案例中使用的是文件流操作,读取保存的xls文件,并且进行数据清洗后再以txt格式的文件保存到本地。进行数据清洗中我执行的任务是排除异常数据,例如特殊格式的字符。通过操纵xls格式的表格数据的工具类我们可以直接读取表格每一个cell的数据,通过正则表达式匹配,能让我们能够去除特定类型的字符串,然后使用replaceAll这个方法能够剔除不合理的字符串。通过这种方式,我们能实现数据清洗的功能。至于如何进行数据格式的转换的功能,我在下一节中详细阐述。

for (int i = 0; i < rownum; i++) {
                    Map<String, String> map = new LinkedHashMap<String, String>();
                    row = sheet.getRow(i);
                    if (row != null) {
                        for (int j = 0; j <= 7; j++) {
                            cellData = (String) getCellFormatValue(row.getCell(j));
                            String regEx="[\n`~!@#$%@#¥%……&*+|{}【】]";
                            String aa = "";
                            String newString = cellData.replaceAll(regEx,aa);
                            map.put(columns[j], newString);
                        }
                    } else {
                        break;
                    }
                    list.add(map);
                }
}

后端Java数据文件格式转换功能

  因为在Java里面实现MapReduce任务最合理的输入文件格式是txt格式的文件,而不是表格格式的文件。表格格式的文件会在文件输入流上带来许多问题。对于如何实现在Java语言里将xls表格形式的数据转换为txt格式的数据。我先通过readExcel的方法将数据导入,然后构建List<Map<String,String>>类型的对象。对于表格的每一行数据,即每一条记录,我们可以抽象成为一个Map类型的数据结构。每一行数据都有对应的列名和数据值。列名就对应着Map<String,String>中的第一个String类型的数据对象,数据值对应着Map<String,String>中的第二个String类型的数据对象。而整张表格则对应着由一定条数的记录构成,所以我们可以使用一个变长数组即List类型的对象来存放Map类型对象的集合。在读取对象的过程中我们可以使用正则表达式等方式来进行上一节中所进行的数据清洗的过程。
  而在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry。Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。所以我们能够通过如下方式从List<Map<String,String>>读取出来每一个String类型的对象存放的东西。

for (Entry<String, String> entry : list.get(i).entrySet()) {
                    String value = entry.getValue();
                    String set = entry.getKey();
}

在这里插入图片描述

后端XGBoost算法设计

  我开发的本平台系统提供了针对当当网平台的图书商品销售情况的预测功能,为商户和平台提供产品的在当月销售情况的预测,商户和当当网平台可以通过系统预测来进行有针对性的营销宣传,并且让平台来根据图书的销售情况进行有针对性的预测。根据业务需求,我们获得了当当网图书畅销排行榜上2021年1月到2021年9月中间9个月的图书数据,然后我们要预测出现在最近7天,最近24小时,最近30天的当当图书畅销排行榜上的图书在2021年11月的图书畅销排行榜上的可能的排名情况。这是一个预测问题,我使用的是XGBoost算法来预测。
  首先我们要明确什么是训练集的数据,什么是预测集的数据。训练集的数据是当当网图书畅销排行榜上2021年1月到2021年9月中间9个月的图书数据,包括排名,价格,浏览次数,推荐指数等特征。预测集的数据是出现在最近7天,最近24小时,最近30天的当当图书畅销排行榜上的图书数据,包括价格,浏览次数,推荐指数等特征。
  首先我们要对原始文件进行一个数据集重组。将当当网图书畅销排行榜上2021年1月到2021年9月中间9个月的图书数据对应的9个文件拼接在一起,作为模型的训练集。
在这里插入图片描述
  首先我们可以使用xlrd库中提供的open_workbook方法来打开在定时爬虫部份保存下来的数据集xls格式的文件,然后使用该book对象的sheet_by_index参数可以获得索引值里面对应的表格,通过pandas库的read_excel方法能够成功将数据文件转换为pandas对象,通过pandas库的concat方法能够将不同的pandas对象拼接起来。
在这里插入图片描述
  接下来我们进行的是特征工程,对字段数据进行各种处理。首先我们删除了对于预测任务本身来讲没有作用效果的图片源地址imag字段,然后再删去了用来标示不同书本的书本名字字段。然后对于出版商这种可能在一张表里重复出现数据的字段来说使用独热编码是最好的方式,我在这里使用了pandas库提供的factorize方法进行独热编码,最后我把这些数据使用了Python的预处理库中的数据转换的功能,使得它们能够成功地在XGBoost算法开始的时候被模型的输入调用。

在这里插入图片描述

  上图是我执行XGBoost任务时候设定的参数。其中eta我经过模型本地的运行以后设计的是0.007,该数值能够让模型的准确率较高,具有良好的分类效果。
  接下来我进行了数据集的划分,ranking字段的数据作为预测的反应变量。
在这里插入图片描述
  最后我调用机器学习的XGBoost的库进行了之前的案例需求中明确的预测任务,并将结果文件导出回本地。
在这里插入图片描述

后端MapReduce任务-序列化与反序列化

  内存中的对象只能本地进程使用,断掉后就消失了,也不能被发送到网络上的另一台机器,序列化可以将内存中的对象发送到远程机器。由于Java本身的序列化框架太重,序列化的对象包含了很多额外信息,不便于在网络中高效传输,Hadoop开发了自己的序列化机制。MapReduce实现的序列化机制,通过实现Writable接口,重写DateInput和DateOutPut方法,实现数据的序列化和反序列化,相比于JDK自带的序列化,MapReduce实现的序列化不包含类的继承关系。而且MapReduce的序列化的机制与JDK的序列化机制也不同,它将对象序列化到流中。JDK的序列化机制是不断的创建对象,但在MapReduce的序列化机制中,用户可以复用对象,这样就减少了java对象的分配和使用垃圾回收机制对不用的java对象进行回收,提高了应用效率。

后端MapReduce任务-统计每家出版商出版的图书数量

  统计每个月图书畅销排行榜中的各个图书出版商出版的图书数目,以此来衡量出版商出版畅销图书的能力,出版的畅销图书越多说明该出版商的能力越强。统计每一家出版社出版的图书的数量,本质上就是统计每个出版社字段出现的次数。而这个问题中,我并没有打算使用序列化与反序列化这样的机制,而是考虑使用文件流尝试直接生成符合模式场景的。因此我们需要进行数据预处理剔除无用字段。除此之外通过爬虫爬取的数据是以xls格式的文件上传到云端HDFS集群进行数据克隆和备份的,如果我们在mapreduce程序中直接使用xls格式文件作为原始数据进行mapreduce任务会带来许多不便,因为通常Java中的文件流并不能直接操作xls格式的文件。所以我们在本地进行格式转换,将xls文件转换成txt文件同时剔除到不需要的字段后上传到云端HDFS集群上,并且作为数据源执行mapreduce任务进行数据统计。Map阶段实现的任务是将”南海出版社”这样的数据直接转化为“<南海出版社,1>”,key为南海出版社是text类型的,value是1为int类型的。
在这里插入图片描述
在这里插入图片描述
  Reduce阶段实现的是将” <南海出版社,1>”这样相同的数据直接计算其个数和作为sum。将sum和key即南海出版社通过调用put对象,往hbase表格里进行插入。
在这里插入图片描述
  在job阶段配置了HBase集群的配置和Hadoop集群的配置,并且配置了源路径和目的路径,从而使得有能力进行Hadoop任务,使用了TableMapReduceUtil从而使得文件能从Hadoop的HDFS集群经过MapReduce任务传向HBase集群。

在这里插入图片描述

前端整体架构设计

  在前端我使用了Vue展现了后端处理好的数据,后端处理好的数据经过数据跨域传向前端,在前端页面上展现出来。在前端上通过表格和Echarts插件作出可视化的图片。在整个Vue工程里面,我使用的是2级页面的形式对它进行处理,下面是我的路由表,和实际页面侧边栏。
在这里插入图片描述在这里插入图片描述

前端表格数据显示-历史数据,预测数据,统计结果

  在前端我使用了Vue表格展现了爬取到了数据,预测的数据和统计的结果数据等。
在这里插入图片描述
  Vue的表格结构如上所示。我使用了Element-UI的table组件,使得表格展现出来的更加美观和谐。
  在编写完表格部份的代码外,我在js的create方法里面编写了使得表格获取到数据的方法。通过axios进行跨域代理,获得后端端口号上传的数据。其中前端端口衔接的后端端口号是http://localhost:8080/x的形式。
在这里插入图片描述在这里插入图片描述

前端表格数据查询

  由于爬取到的原始数据集是2021年1月到9月,总共跨度时长为9个月的数据集,我实现了对原始数据进行按月份切换查询的功能。我们不能单单只观测单个月份的数据,而是要观测多个月份的数据。所以我们应该实现能够根据月份的不同切换数据集的功能。查询的时候是把搜索框searchText的数据传向后端,调用相应的查询方法,将相关数据返回前端。
在这里插入图片描述

前端Echarts延时设计任务设置实现loading效果

  我们知道通过Hive数据仓库进行HQL查询也好,或者通过MapReduce任务执行查询的也好,这些查询功能的代码底层都是通过Map和Reduce任务执行而来的,如果每次想要进行查询任务时都执行查询任务,MVVM架构下的model层的底层后端代码执行查询后再返回前端view层展现出来,这个时间很长,前端页面都会呈现全部空白情况。这在实际项目中是一个大忌,访问该网页的用户可能就会以为该网站出现了bug会直接关掉网页。
在这里插入图片描述
  Echarts提供了showLoading()的方法和hideLoading()的方法,通过在加载数据执行showLoading()的方法,然后设置setTimeout,当该方法获取和后端衔接的数据执行的返回值后,Loading效果将自动消失,直接显示可视化的图片。

前后端数据交互-针对Echarts可视化重新设计的后端接口

  正常来讲前端设计的可视化的图片的数据都是由自变量和因变量两种类型的数据组合起来的。但是一般正常后端往前端传数据时都是使用类似key-value一样的键值对的形式往前端传数据。所以我们应该重新设计后端接口,并于前端的Vue项目里面的代码能够直接使用回传的后端查询HBase集群上得到的数据进行可视化操作作出图像。我在后端工程往前端传时传的是一个List类型的数据,最后一个ArrayList里面存放的是自变量,前面的ArrayList里面存放的是因变量。
在这里插入图片描述

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值