🌟欢迎来到 我的博客 —— 探索技术的无限可能!
前言
-
本项目综合了基本数据分析的流程,包括数据采集(爬虫)、数据清洗、数据存储、数据前后端可视化等
-
推荐阅读顺序为:数据采集——>数据清洗——>数据库存储——>基于Flask的前后端交互,有问题的话可以留言,有时间我会解疑~
-
感谢阅读、点赞和关注
开发环境
- 系统:Window 10 家庭中文版。
- 语言:Python(3.9)、MySQL。
- Python所需的库:pymysql、pandas、numpy、time、datetime、requests、etree、jieba、re、json、decimal、flask(没有的话pip安装一下就好)。
- 编辑器:jupyter notebook、Pycharm、SQLyog。
(如果下面代码在jupyter中运行不完全,建议直接使用Pycharm中运行)
文件说明
本项目下面有四个.ipynb的文件,下面分别阐述各个文件所对应的功能:(有py版本 可后台留言)
-
数据采集:分别从前程无忧网站和猎聘网上以关键词数据挖掘爬取相关数据。其中,前程无忧上爬取了270页,有超过1万多条数据;而猎聘网上只爬取了400多条数据,主要为岗位要求文本数据,最后将爬取到的数据全部储存到csv文件中。
-
数据清洗:对爬取到的数据进行清洗,包括去重去缺失值、变量重编码、特征字段创造、文本分词等。
-
数据库存储:将清洗后的数据全部储存到MySQL中,其中对文本数据使用jieba.analyse下的extract_tags来获取文本中的关键词和权重大小,方便绘制词云。
-
基于Flask的前后端交互:使用Python一个小型轻量的Flask框架来进行Web可视化系统的搭建,在static中有css和js文件,js中大多为百度开源的ECharts,再通过自定义controller.js来使用ajax调用flask已设定好的路由,将数据异步刷新到templates下的main.html中。
技术栈
- Python爬虫:(requests和xpath)
- 数据清洗:详细了解项目中数据预处理的步骤,包括去重去缺失值、变量重编码、特征字段创造和文本数据预处理 (pandas、numpy)
- 数据库知识:select、insert等操作,(增删查改&pymysql) 。
- 前后端知识:(HTML、JQuery、JavaScript、Ajax)。
- Flask知识:一个轻量级的Web框架,利用Python实现前后端交互。(Flask)
二、数据清洗
招聘岗位数据清洗
import re
import pandas as pd
import numpy as np
read_csv读取数据集
data.head()
读取数据
去重去缺失
查看缺失值的缺失比例
data.isnull().mean()
考虑舍弃薪水和工作职责上的缺失数据
data.dropna(subset = ['薪水'], inplace = True)
# 去除重复值
data.drop_duplicates(inplace = True)
# 索引重置
data.index = range(data.shape[0])
data.shape
薪水字段重编码
初始化列表来接收正则提取后的每个字符串
# 初始化列表来接收正则提取后的每个字符串
salary_list = []
for i in range(len(data)):
# 使用正则表达式需要将每一列都转换为字符串形式
data['薪水'][i] = str(data['薪水'][i])
# 注意join之后便为一个字符串,我们使用正则筛选掉数字和其它符号,findall在下列中只会切割每个字,[]表示或,()表示与,{}表示匹配次数
salary_list.append(''.join(re.findall(r'[^0-9\.]', data['薪水'][i])))
# 使用unique知道有多少种写法
np.unique(salary_list)
代码过长这边就大概描述(对薪水这一列的表示方法都统一规范为数字(千/月))
地域字段重编码
对地域这一列进行字段重编码
data['地区'] = data['地域'].apply(lambda x: x.split('-')[0])
data
进行省份的划分
对31个省市进行遍历
人数、学历和经验特征提取
生成人数这一列
num_list = []
k = 0
for i in range(len(data)):
s = str(data['其他信息'][i])
num_str = s.split(' ')[-1]
if re.findall(r'\d', num_str):
num_list.append(int(re.findall(r'\d', num_str)[0]))
else:
num_list.append(np.nan)
k += 1
data['人数'] = num_list
print('招聘人数缺失数量有%s条,占总样本比例为%s' %(k, k / data.shape[0]))
生成学历这一列
k = 0
edu_list = []
for i in range(len(data)):
s = str(data['其他信息'][i])
edu_str = s.split(' ')[-2]
if edu_str in ['博士', '硕士', '本科', '大专', '中专', '高中', '初中及以下']:
edu_list.append(edu_str)
else:
edu_list.append(np.nan)
k += 1
data['学历'] = edu_list
print('学历未标明的数量有%s条,占总样本比例为%s' %(k, k / data.shape[0]))
生成经验这一列
jingyan_list = []
for i in range(len(data)):
try:
ss = data['其他信息'][i].split(' ')[-3]
if '经验' in ss:
if re.findall(r'(.*)\-(.*)\年', ss):
ss_num = re.findall(r'(.*)\-(.*)年', ss)[0]
jingyan = np.round((float(ss_num[0]) + float(ss_num[1])) / 2, 0)
jingyan_list.append(format(jingyan, '.0f'))
elif re.findall(r'(.*)\年', ss):
ss_num = re.findall(r'(.*)\年', ss)[0]
jingyan_list.append(format(float(ss_num), '.0f'))
else:
jingyan_list.append(0)
else:
jingyan_list.append(0)
except:
jingyan_list.append(0)
data['经验'] = jingyan_list
data[:2]
将人数和学历的缺失值全部去除
data.dropna(subset = ['人数', '学历'], inplace = True)
data.index = range(data.shape[0])
去除无意义的字段
data.drop(labels = ['其他信息', '地域', '地区'], axis = 1, inplace = True)
data['公司类型'] = data['公司类型'].fillna(data['公司类型'].mode().values[0])
data.shape
保存数据
data.to_csv('./51job_data_preprocessing.csv', encoding = 'gb18030', index = None)
data.isnull().mean()
清洗岗位上的要求文本
read_csv数据集
df
考虑到工作职责上爬取信息时出现的空缺或爬取不到数据的情况,接下来,我们需要对工作职责这一列进行数据清洗
遍历工作职责这一列,若发现字符串长度小于30,则删除掉这一行的数据,若字符串大于30且匹配到的?占字符串长度的40%以上,同样删除掉这一行的数据
for i in range(len(df)):
df['岗位要求'][i] = str(df['岗位要求'][i])
if len(df['岗位要求'][i]) < 30 or len(re.findall(r'\?', df['岗位要求'][i])) / len(df['岗位要求'][i]) > 0.4:
df.drop(i, axis=0, inplace=True)
# 索引重置
df.index = range(df.shape[0])
df.shape
导入停用词
read_csv
自定义函数来实现分词和去除停用词操作
import jieba
def m_cut(tmpstr):
return [w.strip() for w in jieba.lcut(tmpstr) if w not in stoplist and len(w) > 1]
导入自定义词典
dic = './自定义词典.txt'
jieba.load_userdict(dic)
先去除部分不相关的词汇,之后调用上述函数进行分词及去除停用词的操作
将处理后的数据保存下来
df0.to_csv('./liepin_job_detail.csv', encoding = 'gb18030', index = None)