项目背景
最近在寻找数据分析师的工作,需要了解数据分析师相关岗位的现状,通过招聘网站信息来了解企业的用人要求是最直接也是最简单的途径,这里以Python岗位为例做一个简单的分析报告。
明确分析目的
分析Python岗位近期的招聘情况,包括区域分析、薪资分布、经验要求分布等,了解最新该岗位的招聘情况;利用多元回归建模预测薪资情况。
分析工具
使用Python的Lxml库写爬虫到51job网站爬取数据,分析和可视化均使用Python,主要用到request、lxml、pandas、matplotlib等包。
数据来源
数据来源于51job网站,数据包含字段:'职位’title,'公司名’name,'城市’city,'工资’wage,'发布日期’date,'经验要求’exp,'学历’xueli
数据爬取代码
from urllib import request
from lxml import etree
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import csv
import gevent
from gevent import monkey
monkey.patch_all()
plt.rcParams['font.sans-serif']=['SimHei']
##获取网页
def get_html(url,code):
response = request.urlopen(url)
html = response.read().decode(code)
html = etree.HTML(html)
html = etree.ElementTree(html)
return html
##获取关键字段的信息列表
def get_list(html):
datas = html.xpath('//div[@id="resultList"]/div[@class="el"]')
list = []
for i,data in enumerate(datas):
data = etree.ElementTree(data)
title = data.xpath('/div/p/span/a/@title')[0]
name = data.xpath('/div/span/a/text()')[0]
city = data.xpath('/div/span[2]/text()')[0]
city = city if city else None
wage = data.xpath('/div/span[3]/text()')
wage = wage[0] if wage else None
date = data.xpath('/div/span[4]/text()')[0]
link = data.xpath('/div/p/span/a/@href')[0]
html2 = get_html(link,'gbk')
text = html2.xpath('//p[@class="msg ltype"]/text()')
exp = text[1].strip() if text else None
xueli = text[2].strip() if text else None
head = [title,name,city,wage,date,exp,xueli]
list.append(head)
return list
##把列表信息写入并保存为CSV文件
def write_row(filename,writetype,list):
with open(filename,writetype,newline='') as filename:
writer = csv.writer(filename)
writer.writerows(list)
##写入字段标题名
def write_header(filename,writetype):
with open(filename,writetype,newline='') as filename:
writer = csv.writer(filename)
writer.writerow(('职位','公司','城市','工资','发布日期','经验要求','学历'))
#利用gevent并发和for循环完成1~10页的信息爬取
def grab_one_page(url):
html = get_html(url,'gbk')
list = get_list(html)
write_row('jobs','a',list)
write_header('jobs','w')
tasks = []
for n in range(1,11):
url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,Python,2,{}.html?lang=c&stype=1&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='.format(n)
task = gevent.spawn(grab_one_page,url)
tasks.append(task)
gevent.joinall(tasks)
数据加载与清洗
jobs = pd.read_csv('jobs.csv',engine='python')
jobs.info()
在进行分析之前,需进行数据清洗,有时候数据会出现空缺或其他杂乱情况,需要进行清洗才能够拿来使用。
下图可看出,数据完整没有缺失值。
选取需要的特征
jobs = jobs[['城市','工资','经验要求','学历']]
print(jobs.head(2))
下图,打印前两条看一下字段类型,可以看到城市字段需要进行拆分,保留“-”前面的字符,工资字段需要拆分并计算“-”两端的数值平均值进行代替,经验字段直接保留“-”前面的数字。
## 城市字符串处理函数
def get_area(area):
if '-' in area:
area = area.split('-')
return area[0]
else:
return area
## 经验字符串处理函数
def get_exp(exp):
## 1年经验,2年经验,3-9年经验,无工作经验
if '-' in exp:
exp = exp.split('-')
return exp[0]
elif exp=='无工作经验':
return 0
else:
return exp[0]
## 工资字符串处理函数
def get_wage(wage):
## 三种情况举例:1.5-2千/月,0.4-1.2万/月,12-35万/年
if wage.endswith('千/月'):
wage = wage.replace('千/月','')
wage = wage.split('-')
wage = '%.2f'%((float(wage[0])+float(wage[1]))*1000/2)
return wage
elif wage.endswith('万/月'):
wage = wage.replace('万/月','')
wage = wage.split('-')
wage = '%.2f'%((float(wage[0])+float(wage[1]))*10000/2)
return wage
elif wage.endswith('万/年'):
wage = wage.replace('万/年','')
wage = wage.split('-')
wage = '%.2f'%((float(wage[0])+float(wage[1]))*10000/(2*12))
return wage
## 将上述各处理函数应用到对应列
jobs['城市'] = jobs['城市'].apply(get_area)
jobs['经验要求'] = jobs['经验要求'].apply(get_exp)
jobs['工资'] = jobs['工资'].apply(get_wage)
print(jobs.head(2))
##画出前5个职位需求量最多的城市柱状图
city_count = jobs.groupby(['城市'])['城市'].count()
city_count5 = city_count.sort('city_count.values', ascending=False)
ax = plt.figure(figsize=(10, 8)).add_subplot(111)
sns.barplot(city_count.index, city_count.values, alpha=0.8)
ax.set_ylim([0, 400])
ax.set_xlabel('城市', fontsize=20)
ax.set_ylabel('职位数量', fontsize=20)
ax.set_title('不同城市Python岗位的需求量', size=24)
for x, y in zip(range(5), city_count.values):
ax.text(x, y, '%d'%y, ha='center', va='bottom', fontsize=16, color='b')
plt.show()
由上图可得结:1:一线城市,Python岗位的需求最多,对于想要从事该行业的,一线城市才是好的选择,就业机会大,不过人才比较集中,也意味着竞争压力也较大。
工资分布情况
ax2 = plt.figure(figsize=(10, 6)).add_subplot(111)
sns.distplot(jobs['工资'].astype(float), kde=False, bins=30)
ax2.set_title('工资分布', fontsize=22)
ax2.set_xlabel('工资(元/月)', fontsize=18)
ax2.set_xticks(range(0, 50000, 10000))
plt.show()
由上图可得结论2:从总体薪酬可以看出,Python的收入还是挺可观的。薪酬主要集中在5k-18k,但也有明显的断层,主要分为5k-10k,11K-13K,15K-16K,17K-18K几个阶段,中间有几个小分水岭,起薪还可以,薪酬的提升幅度也很可观。
前5个城市工资比较
group_by_city = jobs.groupby(['城市'])['工资']
df_city = []
for group in city_count.index:
v = group_by_city.get_group(group).values
df_city.append(v)
ax3 = plt.figure(figsize=(10, 6)).add_subplot(111)
sns.boxplot(data=df_city)
ax3.set_xticklabels(city_count.index, fontsize=18)
ax3.set_title('前5个城市工资比较', fontsize=22)
ax3.set_ylabel('工资(元/月)', fontsize=20)
plt.show()
由图可得结论3:北京的工资最高,其次是上海,杭州,深圳,广州。
经验要求分布情况
exp_count = jobs.groupby(['经验要求'])['经验要求'].count()
ax4 = plt.figure(figsize=(10, 8)).add_subplot(111)
sns.barplot(exp_count.index, exp_count.values, alpha=0.8)
ax4.set_ylim([0, 150])
ax4.set_xlabel('经验要求', fontsize=20)
ax4.set_ylabel('数量', fontsize=20)
ax4.set_title('经验要求分布', size=24)
for x, y in zip(range(5), exp_count.values):
ax4.text(x, y, '%d'%y, ha='center', va='bottom', fontsize=16, color='b')
plt.show()
由图可得结论4:2-3年经验的需求比较大,主要集中在这两个年限。从这个情况看,虽然该岗位需求大,但是不能盲目转行,毕竟对经验要求比较大。从长远看,5年以上经验的需求还是比较少的,10年以上近乎无,可以看出5年这个点是比较关键的,职业规划需要在前几年做好,尽快提升自己。
学历分布情况
xueli_count = jobs.groupby(['学历'])['学历'].count()
ax5 = plt.figure(figsize=(10, 8)).add_subplot(111)
sns.barplot(xueli_count.index, xueli_count.values, alpha=0.8)
ax5.set_ylim([0, 300])
ax5.set_xlabel('学历', fontsize=20)
ax5.set_ylabel('数量', fontsize=20)
ax5.set_title('学历分布', size=24)
for x, y in zip(range(5), xueli_count.values):
ax5.text(x, y, '%d'%y, ha='center', va='bottom', fontsize=16, color='b')
plt.show()
由图可得结论5:Python对本科生的需求最大,其次是大专,其他学历需求很少,对于博士需求基本没有。
工作经验与薪酬相关情况
sns.boxplot(jobs['经验要求'].astype(int),jobs['工资'].astype(float))
plt.xlabel('经验要求')
plt.ylabel('工资')
plt.title('工作经验与薪酬相关情况',size=24)
plt.show()
学历与薪酬相关情况
sns.boxplot(jobs['学历'],jobs['工资'].astype(float))
plt.xlabel('学历')
plt.ylabel('工资')
plt.title('学历与薪酬相关情况',size=24)
plt.show()
由上图可得结论6:随着工作经验的增加,相应的工资也会随着增加。随着学历的上升,相应的工资也会随着增加。这些均符合我们的常识判断。
总结
通过分析,我们可以得出一些结论:
1、Python就业机会主要集中在一线城市,如上海、深圳、北京等城市;
2、Python岗位的薪资主要集中在5k-18k,中间有几个小分水岭,薪资有较大的发展空间,且一线城市中北京的工资最高,其次是上海,杭州,深圳,广州。
3、Python岗位的经验要求集中在2-3年,该岗位对经验方面比较看重,且工作经验和工资呈正相关。值得注意的是从长远看,5年以上经验的需求还是比较少的,可以看出5年这个时间点是比较关键的,需要做好职业规划。
4、Python在学历方面对本科生的需求最大,其次是大专,其他学历需求很少,对于博士需求基本没有,在此基础上学历和工资呈正相关。
多元线性回归模型预测薪资
from sklearn.preprocessing import LabelEncoder as LE
from sklearn.linear_model import LinearRegression as LR
#将各字符分类变量重编码为数值分类变量
le = LE()
jobs['城市'] = le.fit_transform(jobs['城市'].values) #城市重编码
jobs['学历']=jobs['学历'].replace('中专',0) #学历重编码
jobs['学历']=jobs['学历'].replace('高中',1)
jobs['学历']=jobs['学历'].replace('大专',2)
jobs['学历']=jobs['学历'].replace('本科',3)
jobs['学历']=jobs['学历'].replace('硕士',4)
## 特征选择
X = jobs[['城市','经验要求','学历']]
## 结果集
y = jobs['工资']
## 模型学习
model = LR()
model.fit(X,y)
##构建预测函数
def wage_pred(area,exp,xueli):
area_index = list(le.classes_).index(area)
xueli_index = list(('中专','高中','大专','本科')).index(xueli)
wage_pred = model.predict([[area_index,exp,xueli_index]])
print('%.2f'%wage_pred)
## 测试代码
if __name__ == '__main__':
wage_pred('北京',2,'本科')