流程

数据采集→数据处理→数据分析→应用

首先,通过爬虫抓取 51Job ,智联招聘等网站有关大数据的职位数据。通过爬虫得到的数据是非常乱的,即便我们在保存的时候已经尽量规定好了格式,所以要对数据进行清洗,把数据格式化。(也许后面还应该去除重复,错误的数据…)

现在我们得到一堆数据了,一方面我们可以对这些数据进行数据挖掘,找到数据间的高价值信息(比如说求职地对工资的影响啦什么的)。其次,我们还可以根据这些信息,与用户的信息进行对比,预测用户的工资区间,为用户推荐工作什么的…

有了一堆高价值信息,要展现啊,而且还得与用户进行交互(以做预测,推荐)。这一块通过搭建网站来实现。

任务分配

森林爬虫,波波老师数据分析,挖掘,我搭建网站。

爬虫

用的 jsoup。

可以对 51Job, 智联招聘这两个网站进行数据采集。设置了几个参数,可以与网站后台管理交互。

通过后台来管理爬虫爬取的频率,数据存放的目录什么的…

爬虫保存的文件的方式是每多少个数据生成一个 json 文件,文件名以 1.json,2.json …这样命名。(为了方便后续步骤)

清洗,挖掘,分析

这一块的细节我不是很清楚

0x01

搞了 3 台虚拟机(Centos)搭建了个 Hadoop 集群。

一开始选择的是 Hadoop,后面发现 Hadoop 对离线计算支持的很好,要是搞实时计算,用 Spark 比较好。(也不知道具体情况。Spark 没用。)

使用 flume 监听爬虫采集的存放目录(理论上是可行的,不过没研究虚拟机怎么和本机目录交互,所以没搞。),当爬虫开始执行,该目录就不断生成职位数据,每当有新数据产生,flume就会把文件采集到 hdfs 中去储存。

在此,通过 Hadoop MapReduce 对离线数据进行清洗,得到优质的,格式化的数据。

0x02

然后我们把数据从 hdfs 导入 hive,进行数据挖掘。在这一步,我们首先对数据进行聚类,大致找出这些独立数据之前的联系,再通过手动筛选,剔除那些不符合我们预期的信息,再用已有数据对数据进行训练,以此得到精确度较高的聚类手段与符合期望的数据。(聚类了,但是剔除什么的没做…)

0x03

hive 可以把数据导入到数据库,我们把清洗后的职位数据导入到 MySql ,然后就能基本展现一些东西了…

0x04

这里和前台页面联系一下。

比如我们想在前台用直方图的形式展示求职地与工资的关系,如果我们在后端进行计算,每打开一次展示的页面就要计算一次,太耗费资源了(主要是显示太慢了,到时候录视频展示项目岂不是很尴尬。)。

而且这类数据变动也不大,所以就在数据处理这一块先把这类信息计算出来,再导入数据库的某张表里。到时候显示就只需直接取表的数据,不用进行多余的计算了。

搭建

前端写的太差劲了。后台管理用的开源页面,还可以。

数据可视化展示

  • 图表展示 : ECharts
  • 表格展示 : Bootstrap Table

展示了啥?

具体展示了啥

工资 - 地区 关系

工资 - 学历 关系

岗位能力需求词云

基本的信息检索

前端

不过插件使用倒是熟练了不少,插件版本不兼容什么的问题也遇到一大堆。

jquery 插件到哪里下载呢

标签插件

表单验证插件

图表,表格插件

还有啥?

没了…

后端

采用 Spring Boot,Idea 创建一个 Spring Boot 项目很简单,有 Spring Initializr。

写了一个小小的 Demo,感觉用了不少东西,其实也没多少,都只局限于知道这个工具是干啥的的阶段。

  • 项目自动化构建工具:Gradle

​ 源到 maven 仓库 找。

  • 权限管理:Spring Security

用于判断登录用户的访问权限,比如区分普通会员和管理员之类的。想要自定义使用的话,好麻烦哦。

之前记录了一些使用方式,还没写完…

Java 模板引擎:Thymeleaf

想到如果数据全都通过 json 来传,模板的作用是不是就不大了?

[update-2019-10-09] 没错啊,这就是前后端分离…

  • 持久层框架:MyBatis

怎么配置。

mybatis-genetator 插件,用于自动把数据库里的表生成 bean(提供了 getter,setter的 java 类),Mapper(通过 mabatis 与数据库交互相关的接口,比如增删查这个表里的东西…没什么大用,只是节省了创建接口的时间,具体的 SQL 还得自己写。)

数据库查询还用了分页插件 pagehelper…配合前端的表格插件,实现显示数据的分页功能。

  • debug 工具

log4j2

  • fastjson

SpringMVC 自带的 json 工具不够用…

值得一提的就是学会了写一个稍微有点复杂的 SQL 语句。(波波老师写的,模仿了一下,收获不少。)

其他的收获都是对工具的熟练…

预测与推荐

预测

这一步在后端来实现,不过波波老师写了一个 R 的脚本放在服务器,我直接远程调用。

原理是进行多元回归。

这里是用户传来的数据与 Hadoop 中数据交互计算…

Kmeans聚类:程序调用 mahout 算法库API实现

回归建模:工资(Y)~ 学历(X1)+求职地(X2)+ 岗位类别(X3)+经验(X4)+企业性质(X5)

推荐

怎么做推荐呢?实际开发过程中,我们一边学习推荐相关的知识,一边先按直觉做了几个 demo。

在第一个版本中,用模糊查询实现。根据用户的相关信息到数据查询。大概是因为逻辑判断太多了,速度非常慢。

第二个版本,尝试优化查询语句,建立索引。

第三个版本。因为前面已经通过多元回归计算出了用户的工资区间(虽然很不准确就是了),我们拍脑袋觉得对于一份工作来说,工资是很重要的,于是直接按工资区间到数据库查询…

前两个版本,模糊查询的推荐结果还不错,至少能一眼看出与用户的信息有关。但是速度实在太慢了。

第三个版本,因为前面做的预测结果本来就不准,所以效果更差劲…而且这个思路太想当然了。

但至少现在已经有这样一个功能模块了!!于是接着尝试真正的推荐。

我理解的推荐

因为水平不到位,不能从比较宏观的角度解释,下面都只是我在这个项目中的感受。所以,推荐以「推荐 Job」为例…

我理解的推荐,就是为用户建立一个模型——「我最喜欢的 Job」。系统不断优化这个模型,试图找到用户心中最中意的那个 Job。(这只是个理想化的模型,实际的系统当中可能并不存在)然后从系统中找出与这个模型最匹配的 Job,推荐给用户。

那么问题来了,计算机怎么判断用户是不是中意某个 Job 呢?

我们看看协同过滤算法中的基于用户的推荐算法。

基于用户的推荐算法

最简单的一种,如下:

小明 喜欢 A、B、C。
小红 喜欢 A、B、C、D。

我们发现小红的喜欢和小明的喜欢重叠度很高,小明可能也会喜欢 D,所以把 D 推荐给他。

但是这种判断的粒度太大了,再细一点可以这样:

小明 非常喜欢 A
小明 有点喜欢 B

小红 非常喜欢 B
小红 有点喜欢 A

在计算机中,可以简化为这样的三元组 < 用户 ID, 物品 ID, 用户偏好 > 表示:

用户   JobID   偏好值(权重)
小明    001    15
小明    002     0

小红    001     0
小红    002     15

那么这个偏好值怎么来呢?

肯定需要严格的算法…

我们可以粗略举个例子来理解,比如用户点击一次某个职位,我们假定他是被这个职位所吸引,把他与这个职位的权重相应增加。

这样在计算机内部就得到用户与 Job 的关系。

用这样的数值来量化之后,我们就可以通过做一些矩阵运算,根据用户自己的列表和别的用户的列表,把口味相似的用户关注的职位推荐给用户。

好了!暂且到这一步。

我们回过头来看,基于用户的推荐算法,需要满足两项。

  1. 用户(使用推荐功能的用户)的喜好
  2. 别的用户的喜好

这就是冷启动需要解决的问题。

它不仅需要用户自己有信息,还需要别的用户有信息…

考虑到我们做的这个小项目根本不可能有那么多用户,测试起来也麻烦,看来这个方式不合适…

基于内容的推荐算法

基于用户的推荐算法,是通过比较各个用户的喜好,把别的用户关注的东西推荐给有共同喜好的用户。

而基于内容的推荐算法,就是把用户的喜好直接与 Job (物品)比较。

还是看个最简单的:

小红       喜欢 哲学书籍
《大问题》  是   哲学书籍

所以把《大问题》推荐给小红。

同样,我们可以更细粒度一点:

小红       非常喜欢   哲学书籍
小红       有点喜欢   小说

《大问题》   是       哲学书籍
《爱的教育》 是       小说

我们优先推荐《大问题》给小红…

而在计算机内,可能是这样表示的:

用户		标签		权重
小红		Java		 10
小红		Web		 	 9

Job			标签		权重
001			Java		 0
001			Web			10
002			Java		10
002			Web			9

通过计算向量距离,得出用户和 Job 的相似度,生成推荐。

基于内容的推荐需要解决两个问题。

第一个仍然是冷启动,用户需要打上标签。我们考虑过根据用户的行为——历史记录,点击记录等来生成用户标签。

不过都没时间做…(太真实了)

其实提到这个场景,我想到的是各种信息流 app,注册时让用户选择一堆感兴趣的话题,相当于打上标签。于是我也模仿这样做了个…

我们只做到了用户和某个标签产生联系,如标签 HadoopJava

存在数据库里 :

用户 ID		标签Id		有无 // 标志用户有没有选择某个标签。。。
001				001				0
001				002				1
...

第二个问题就是提取出 Job 的标签,并且提取权重。

这个是重点也是难点…

从抽象的角度来看,Job 其实也只是一串文本描述的一样物品。

粗略地做,我们可以进行简单的词频统计…

常用的更标准的方式是使用 TF-IDF。

比较

SQL 模糊查询是基于元数据的推荐。比如推荐书,紧紧按出版年份来推荐是不太合适的。(大多数情况下)

而协同过滤,是基于标签,更合理些(理论上)。

我们做出来的效果,感觉模糊查询似乎看起来科学点…至少能在推荐的结果找到我们的输入的信息。

通过后面的方式得到的推荐结果(因为我们技术不到位的原因)看起来有点没头没脑…