An open API service indexing awesome lists of open source software.

https://github.com/hscspring/suitjob

A Simple Job Recommendation App. Also a Micro-Web-Full-Stack Practice for Beginners.
https://github.com/hscspring/suitjob

full-stack full-stack-web-development hug react umi

Last synced: about 2 months ago
JSON representation

A Simple Job Recommendation App. Also a Micro-Web-Full-Stack Practice for Beginners.

Awesome Lists containing this project

README

          

## 使用说明

做了个小 [Demo](https://sj.yam.gift),项目的初衷是根据招聘网站发布的职位说明和职位发布情况,通过让用户从随机给出的职位职责和要求描述中分别选择自己喜欢和具备的描述,然后根据语料中不同岗位(一般是同一类职位的统称)词频统计计算用户选择得分,最后按得分从高到低排序后将 topK 输出。

另外一个初衷是实践一个全栈的精简版微服务框架:

- 后端采用基于 [Falcon](https://github.com/falconry/falcon) 的 [Hug](http://www.hug.rest/),性能出色(最快的框架之一)而且很轻
- 前端采用 [UmiJS](https://umijs.org/zh/),非常出色的企业级 react 应用框架

## 开发部署

```bash
# frontend
yarn # install
yarn start # start
yarn build # deploy
# backend
pipenv install # or
pip install -r requirements.txt # install
hug -f api.py # start
uwsgi --http 0.0.0.0:8000 --wsgi-file api.py --callable __hug_wsgi__ # deploy
```

## 数据获取

> 注意:
>
> - 岗位是指同一类职位的总称。
>
> - 爬取时间点为 2019 年 5 月 12 日。

### Step1:获取所有类别

- 人工获取岗位中文名类别(共 8 大类,39 小类,302 个岗位)
- 通过中文名拼音获取类别网址 URL(部分需要人工修改)
- 文件夹名称为:`category`

### Step2:获取每个岗位的 URL list

- 共 302 个岗位的 URL list,每个岗位一个文件

- 文件夹名称为:`position_urlist`,对应代码:`get_urlist.py`

- 部分类别 urlist 有重复(第一个数字是去重后的 url 数):

```json
技术_后端开发_区块链_qukuailian.txt 430 435
技术_企业软件_售前工程师_shouqiangongchengshi.txt 423 435
职能_行政_助理_zhuli.txt 434 435
技术_运维_运维开发工程师_yunweikaifagongchengshi.txt 420 435
设计_视觉设计_原画师_yuanhuashi.txt 421 435
技术_后端开发_Go_go.txt 417 435
技术_运维_系统安全_xitonganquan.txt 425 435
游戏_其他_电竞主持_dianjingzhuchi.txt 428 435
技术_后端开发_Python_Python.txt 433 435
运营_运营_运营专员_yunyingzhuanyuan.txt 433 435
产品_产品设计师_无线产品设计师_wuxianchanpinshejishi.txt 92 99
产品_产品经理_产品实习生_chanpinshixisheng.txt 413 424
技术_后端开发_全栈工程师_quanzhangongchengshi.txt 422 435
运营_运营_活动运营_huodongyunying.txt 434 435
技术_硬件开发_电路设计_dianlusheji.txt 434 435
产品_产品经理_产品经理_chanpinjingli.txt 433 435
运营_运营_运营经理_yunyingjingli.txt 434 435
职能_财务_财务_caiwu.txt 433 435
技术_后端开发_C++_C++.txt 430 435
技术_高端职位_高端技术职位其它_gaoduanjishuzhiweiqita.txt 431 435
运营_运营_产品运营_chanpinyunying.txt 389 435
产品_产品经理_数据产品经理_shujuchanpinjingli.txt 427 435
设计_用户研究_数据分析师_shujufenxishi.txt 426 435
产品_产品经理_产品助理_chanpinzhuli.txt 428 435
```

### Step3:下载所有职位页面

- 共 92842 个(不重复 57905 个)职位页面(有些职位同时属于不同的岗位)
- 文件夹名称为:`html`,对应代码:`spider.py`

### Step4:解析 html 文件

- 每个 html 解析结果包括以下数据:

- category:大类+小类+岗位,下划线连接
- pname:对应 html 文件的位置
- company:公司名称
- jobname:职位名称
- salary:工资
- labels:标签
- request:基本要求(要求概要)
- jobadv:职位诱惑
- duty:职位职责
- require:职位要求
- jobdetail:职位描述
- dtime:职位发布时间

- jobdetail 包括 duty 和 require,自动做了分割,但有些没能分开的 duty 和 require 字段留空

- 使用职责和要求的不同描述进行分割,不同描述文件名为:`job_description_mark_sorted_dict.txt`
- 使用 `preprocess.py` 获取

- 解析结果样例:

```json
{
"category": "技术_后端开发_Go",
"pname": "./data/html/技术_后端开发_Go_go/5188451.html",
"company": "讯码科技招聘",
"jobname": "go/golang开发工程师",
"salary": "12k-18k ",
"labels": "Golang,C++,GO,Shell",
"request": "广州,经验3-5年,本科及以上,全职",
"jobadv": "职位诱惑:13薪以上,牛人多,福利全",
"duty": "职位职责::1.使用Golang开发服务端应用;2.参与基础组件开发及框架改进工作;3.与产品有效沟通,理解需求,完成功能开发;4.主动跟进项目,完善功能,优化性能。",
"require": "职位要求::1.本科及以上学历,计算机、通信等相关专业,3年以上Golang相关开发经验;2.掌握Shell/Python脚本,熟悉C/C++语言;3熟悉TCP/IP协议,了解HTTP协议,有网络编程经验者优先;4.熟悉Mysql,有Redis使用经验者优先;5.熟悉Linux操作系统,熟悉常用Linux命令;6.对技术有激情,喜欢钻研,能快速接受和掌握新技术,学习能力和工作责任心强,良好的沟通表达能力和团队协作能力。",
"jobdetail": "\n职位描述:\n1. 使用Golang开发服务端应用;\n2. 参与基础组件开发及框架改进工作;\n3. 与产品有效沟通,理解需求,完成功能开发;\n4. 主动跟进项目,完善功能,优化性能。\n\n职位要求:\n1. 本科及以上学历,计算机、通信等相关专业,3年以上Golang相关开发经验;\n2. 掌握Shell/Python脚本,熟悉C/C++语言;\n3 熟悉TCP/IP协议,了解HTTP协议,有网络编程经验者优先;\n4. 熟悉Mysql,有Redis使用经验者优先;\n5. 熟悉Linux操作系统,熟悉常用Linux命令;\n6. 对技术有激情,喜欢钻研,能快速接受和掌握新技术,学习能力和工作责任心强,良好的沟通表达能力和团队协作能力。\n",
"dtime": "2019-05-12"
}
```

- 文件夹名称为:`extract`,对应代码:`parse.py`

### Step5:分词和获取词性

- 使用百度 API 进行分词和获取词性:[百度 AI 开放平台 - 全球领先的人工智能服务平台 - 百度 AI 开放平台](http://ai.baidu.com/docs#/NLP-Python-SDK/top)
- 文件夹名称为 `segpos`,对应代码:`segpos.py`

以上结果下载链接: https://pan.baidu.com/s/1D2qd6N0CPkEYQ-I37t7G8A 提取码: wxim

## 模型构建

主要思想:获取不同类别下职位职责、职位要求的高频描述以及发布情况;职位职责对应兴趣,职位要求对应能力,发布情况对应市场需求,根据不同的权重计算得分。

- 只计算大类和小类
- 使用 Bi-gram 作为描述计算词频概率(取前 100 个)
- 发布情况计算连续发布频次、离散发布频次和发布比
- 连续发布频次:职位数/日期间隔连续天数
- 离散发布频次:职位数/日期间隔离散天数
- 发布比:日期间隔离散天数/日期间隔连续天数

## 算法描述

- 随机分别生成 56 个职责描述和 56 个要求描述

- 从 8 个大类中生成,每个大类 7 个
- 使用均匀分布

- 用户选择任意数量的职责描述和要求描述

- 样例(随机选择 7 个):

```json
Choose Duty: [('数据仓库', '技术'), ('图像处理', '技术'), ('培训计划', '职能'), ('整体设计', '设计'), ('流程设计', '产品'), ('广告客户', '市场or营销'), ('业务流程', '销售')]
Choose Require: [('抗压能力', '技术'), ('敏感度', '运营'), ('策划经验', '产品'), ('互联网产品', '设计'), ('管理能力', '市场or营销'), ('服务器开发', '游戏'), ('责任心强', '市场or营销')]
```

- 根据职责描述获取每个所选词大类的分数(直接使用概率),最终获得不同大类的总分数(概率和)

- 职责对应兴趣,到大类即可

- 同一个描述可能属于多个大类

- 部分大类可能没有分数,样例:

```json
{ '产品': 0.010487580496780129,
'市场or营销': 0.005943604865462588,
'技术': 0.016414329444858114,
'职能': 0.008015690287371023,
'设计': 0.005027564924933255,
'销售': 0.0060920762122822795}
```

- 根据要求描述获取每个所选词小类的分数,最终获得不同小类的总分数

- 要求对应能力,需要到小类

- 同一个描述可能属于多个小类

- 部分小类可能没有分数,样例:

```json
{ '产品': {'产品经理': 0.011887362369677482},
'市场or营销': {'品牌or广告': 0.027307293466989284,
'媒介or公关': 0.03395867964853954,
'市场营销': 0.03229166666666666,
'渠道or推广': 0.025292443882390138,
'高端职位': 0.03980288097043215},
'技术': {'DBA': 0.02053442148216401,
'人工智能': 0.01198520974117047,
'企业软件': 0.0314337862502535,
'前端开发': 0.020643112346169116,
'后端开发': 0.014797100366190868,
'测试': 0.02280685151887953,
'硬件开发': 0.018153924827409868,
'移动开发': 0.015384615384615385,
'运维': 0.015524342591614743,
'项目管理': 0.03275602409638554,
'高端职位': 0.016548212580884694},
'游戏': {'其他': 0.0055055973573132685,
'技术开发': 0.007836990595611285,
'设计': 0.006876045344731463},
'设计': {'交互设计': 0.03861386138613861,
'用户研究': 0.009928021841648052,
'视觉设计': 0.011181679935593523},
'运营': {'运营': 0.008169416243654823}}
```

- 根据发布情况获取每个词对应小类的分数,不求和

- 发布对应市场,需要到小类(其实最好能到岗位)

- 同一个描述可能属于多个小类,但同一个小类只计算一次

- 也可以根据上一步得到的结果,直接获取每个小类的分数

- 分数 = (连续发布概率 + 离散发布概率)/ 2 × 发布比

- 注意这里使用概率而非频次,因此需要事先计算总频次

- 部分小类可能没有分数,样例:

```json
{ '产品': {'产品经理': 0.03338227327760723},
'市场or营销': {'品牌or广告': 0.04087859426280987,
'媒介or公关': 0.012720379195385842,
'市场营销': 0.02944734892048413,
'渠道or推广': 0.010658943804001439,
'高端职位': 0.017418657259696938},
'技术': {'DBA': 0.029649446943291725,
'人工智能': 0.03324076360700673,
'企业软件': 0.02034407390155603,
'前端开发': 0.024170608980951473,
'后端开发': 0.06683288768041418,
'测试': 0.036024386612371125,
'硬件开发': 0.03878595493328742,
'移动开发': 0.016586921155638965,
'运维': 0.03711150271059644,
'项目管理': 0.007576210056765727,
'高端职位': 0.03183617363908635},
'游戏': {'其他': 0.018234585928978805,
'技术开发': 0.013639828450524008,
'设计': 0.017438629527587714},
'设计': {'交互设计': 0.016415752451585802,
'用户研究': 0.012479173517052149,
'视觉设计': 0.058576892397684745},
'运营': {'运营': 0.04485727467672736}}
```

- 根据上面得到的三个分数得到最终的分数

- 计算公式为:职责 × 0.5 + 要求 × 0.3 + 需求 × 0.2,意为:兴趣 50%,能力 30%,市场 20%

- 最终结果分数归一化后排序:

```json
[('产品-产品经理', 100),
('产品-产品设计师', 91),
('运营-运营', 90),
('产品-高端职位', 88),
('技术-后端开发', 88)]
```

## 一些问题

- 对职业职责的描述太 “职业化”,导致很多描述看起来就像是那一类型的工作,这在一定程度上失去了判断的意义(如果我知道自己喜欢这些描述,自然就知道自己喜欢这样的工作);而实际情况往往还要更加复杂。
- 职业要求中会出现相似度比较高的词,比如 “大学本科”、“统招本科”、“全日制本科” 之类。
- 计算公式中各项的权重根据先验知识设置,不一定有效。