招聘数据分析
作为大数据方向的学生,了解市场对大数据相关岗位的需求,对将来的就业和职业规划很有帮助。现要求从招聘网站,爬取大数据相关岗位的招聘信息,对招聘信息进行处理和分析,并进行可视化展示。
任务一:数据获取与存储
点此查看爬虫代码或者在网页下方查看代码
从“前程无忧”招聘网站获取大数据相关岗位的招聘信息,并分别存储至数据库和csv文件中。
具体要求:
1.从网址:https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE,2,1.html 入口获取所有的大数据相关岗位招聘信息。如从多个招聘网站获取更多招聘信息更好。
提取数据项至少包括以下字段:
(1)职位名称(岗位名称)、公司名称、 工作地点、薪资(底薪-上限)、发布时间(月-日);

(2)工作年限要求,学历要求,招聘人数
(3)公司性质 公司规模(人数) 公司所属行业
(2)和(3)字段对应网页位置:

说明:如从招聘列表处点击进入详细招聘信息,进入的不是前程无忧网页模板,则不用获取。
爬虫代码
1.使用类
import requests
from lxml import etree
from pymongo import MongoClient
class WuyouSpider:
def __init__(self):
'''初始化'''
# 职位链接url地址 从1开始
self.url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE,2,{}.html'
self.file = open('qiancheng.csv', 'w', encoding='utf-8')
# 连接mongodb数据库
mongo = MongoClient(host='172.26.97.83', port=27017)
db = mongo['qianchengwuyou']
self.cursor = db['wuyou']
self.file.write(
'{},{},{},{},{},{},{},{},{},{},{}\n'.format('work_name', 'company_name', 'edu_level', 'work_require_people',
'work_exp', 'release_date',
'work_location', 'work_salary', 'company_type', 'company_size',
'company_industry'))
def get_response(self, url):
'''获取response响应'''
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'
}
response = requests.get(url,headers=headers)
response.encoding = 'gbk'
return response.text
def parse_list(self, data):
'''处理数据,消除列表符号'''
return ''.join(data)
def parse_work_info(self, url):
'''根据职位链接获取职位信息'''
response = self.get_response(url)
html_content = etree.HTML(response)
# 工作年限要求,学历要求,招聘人数 职能类别
work = {}
# 职位名称(岗位名称)、公司名称、 工作地点、薪资(底薪-上限)、发布时间(月-日);
work['work_name'] = self.parse_list(html_content.xpath("//div[@class='in']/div[@class='cn']/h1/@title"))
work['company_name'] = self.parse_list(
html_content.xpath("//div[@class='in']/div[@class='cn']/p[@class='cname']/a/@title"))
# 结果为一串字符,需要进行分割 并去除空字符
content1 = self.parse_list(
html_content.xpath("//div[@class='in']/div[@class='cn']/p[@class='msg ltype']/@title")).split('|')
content1 = [i.strip() for i in content1]
# 提取结果 ['广州-天河区', '5-7年经验', '本科', '招1人', '06-03发布']
for con in content1:
if con in ['初中及以下', '高中/中技/中专', '本科', '无工作经验', ' 大专']:
work['edu_level'] = con
if con.find('招') != -1:
work['work_require_people'] = con
if con.find('经验') != -1:
work['work_exp'] = con
if con.find('发布') != -1:
work['release_date'] = con
for column in ['edu_level', 'work_require_people', 'work_exp', 'release_date']:
if column not in work.keys():
work[column] = None
work['work_location'] = content1[0]
work['work_salary'] = self.parse_list(html_content.xpath("//div[@class='in']/div[@class='cn']/strong/text()"))
# 公司性质 公司规模(人数) 公司所属行业
work['company_type'] = self.parse_list(html_content.xpath("//div[@class='com_tag']/p[1]/@title"))
work['company_size'] = self.parse_list(html_content.xpath("//div[@class='com_tag']/p[2]/@title"))
work['company_industry'] = self.parse_list(html_content.xpath("//div[@class='com_tag']/p[3]/@title")).replace(
',', ' ')
# 将数据写入mongodb数据库
self.cursor.insert_one(work)
print(work)
# 将数据写入csv
self.file.write(
'{},{},{},{},{},{},{},{},{},{},{}\n'.format(work.get('work_name') or '空', work.get('company_name') or '空',
work.get('edu_level') or '空',
work.get('work_require_people') or '空',
work.get('work_exp') or '空',
work.get('release_date') or '空',
work.get('work_location') or '空',
work.get('work_salary') or '空', work.get('company_type') or '空',
work.get('company_size') or '空',
work.get('company_industry') or '空'))
if len(work) < 2:
return False
else:
return True
def parse_page_href(self, page):
'''获取页面内职位链接
参数: page 页数(从1开始)
'''
url = self.url.format(page)
response = self.get_response(url)
html_content = etree.HTML(response)
# 提取链接
works_url = html_content.xpath("//div[@class='dw_table']//div[@class='el']/p/span/a/@href")
# 根据职位链接获取职位信息
for url in works_url:
resu = self.parse_work_info(url)
if resu is False:
print('爬取结束')
return False
def run(self):
'''爬取前程无忧大数据相关岗位招聘信息
步骤:
1.获取第一页所有职位链接
2.根据职位链接获取职位信息
3.获取下一页职位链接
4.根据职位链接获取职位信息...
'''
# 1.获取第一页所有职位链接
page = 1
# while 循环中的page用于控制爬取页面数 此处小于三页
while page < 3:
resu = self.parse_page_href(page)
if resu is False:
break
page += 1
self.file.close()
if __name__ == '__main__':
wuyou = WuyouSpider()
wuyou.run()
2.使用函数
import requests
from lxml import etree
from pymongo import MongoClient
# 职位链接url地址 从1开始
index_url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE,2,{}.html'
file = open('qiancheng.csv', 'w', encoding='utf-8')
# 连接mongodb数据库
mongo = MongoClient(host='172.26.97.83', port=27017)
db = mongo['qianchengwuyou']
cursor = db['wuyou']
file.write('{},{},{},{},{},{},{},{},{},{},{}\n'.format('work_name', 'company_name', 'edu_level', 'work_require_people',
'work_exp', 'release_time',
'work_location', 'salary', 'company_type', 'company_size',
'company_industry'))
def get_response(url):
'''获取response响应'''
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'
}
response = requests.get(url, headers=headers)
response.encoding = 'gbk'
return response.text
def parse_list(data):
'''处理数据,消除列表符号'''
return ''.join(data)
def parse_work_info(url):
'''根据职位链接获取职位信息'''
response = get_response(url)
html_content = etree.HTML(response)
# 工作年限要求,学历要求,招聘人数 职能类别
work = {}
# 职位名称(岗位名称)、公司名称、 工作地点、薪资(底薪-上限)、发布时间(月-日);
work['work_name'] = parse_list(html_content.xpath("//div[@class='in']/div[@class='cn']/h1/@title"))
work['company_name'] = parse_list(
html_content.xpath("//div[@class='in']/div[@class='cn']/p[@class='cname']/a/@title"))
# 结果为一串字符,需要进行分割 并去除空字符
content1 = parse_list(html_content.xpath("//div[@class='in']/div[@class='cn']/p[@class='msg ltype']/@title")).split(
'|')
content1 = [i.strip() for i in content1]
# 提取结果 ['广州-天河区', '5-7年经验', '本科', '招1人', '06-03发布']
for con in content1:
if con in ['初中及以下', '高中/中技/中专', '本科', '无工作经验', ' 大专']:
work['edu_level'] = con
if con.find('招') != -1:
work['work_require_people'] = con
if con.find('经验') != -1:
work['work_exp'] = con
if con.find('发布') != -1:
work['release_time'] = con
for column in ['edu_level', 'work_require_people', 'work_exp', 'release_time']:
if column not in work.keys():
work[column] = None
work['work_location'] = content1[0]
work['salary'] = parse_list(html_content.xpath("//div[@class='in']/div[@class='cn']/strong/text()"))
# 公司性质 公司规模(人数) 公司所属行业
work['company_type'] = parse_list(html_content.xpath("//div[@class='com_tag']/p[1]/@title"))
work['company_size'] = parse_list(html_content.xpath("//div[@class='com_tag']/p[2]/@title"))
work['company_industry'] = parse_list(html_content.xpath("//div[@class='com_tag']/p[3]/@title")).replace(',', ' ')
# 将数据写入mongodb数据库
cursor.insert_one(work)
print(work)
# 将数据写入csv
file.write(
'{},{},{},{},{},{},{},{},{},{},{}\n'.format(work.get('work_name') or '空', work.get('company_name') or '空',
work.get('edu_level') or '空',
work.get('work_require_people') or '空', work.get('work_exp') or '空',
work.get('release_time') or '空', work.get('work_location') or '空',
work.get('salary') or '空', work.get('company_type') or '空',
work.get('company_size') or '空',
work.get('company_industry') or '空'))
if len(work) < 2:
return False
else:
return True
def parse_page_href(page):
'''获取页面内职位链接
参数: page 页数(从1开始)
'''
url = index_url.format(page)
response = get_response(url)
html_content = etree.HTML(response)
# 提取链接
works_url = html_content.xpath("//div[@class='dw_table']//div[@class='el']/p/span/a/@href")
# 根据职位链接获取职位信息
for url in works_url:
resu = parse_work_info(url)
if resu is False:
print('爬取结束')
return False
def run():
'''爬取前程无忧大数据相关岗位招聘信息
步骤:
1.获取第一页所有职位链接
2.根据职位链接获取职位信息
3.获取下一页职位链接
4.根据职位链接获取职位信息...
'''
# 1.获取第一页所有职位链接
page = 1
while page:
resu = parse_page_href(page)
if resu is False:
break
page += 1
file.close()
if __name__ == '__main__':
run()
任务二:数据清洗
代码在题目下方(注意:代码内的部分内容可能与上面爬虫所爬取的数据不同(如字段、文件名),请根据实际情况修改)
1.规范岗位名称
有的岗位名称含有()或者()或者- 或者空格 或者/,补充说明地点,背景方向要求,福利,学历等信息。把岗位名称中的补充信息全部去掉。
2.从工作地点中提取出城市名
将含有“异地招聘”字样或工作地点缺失的招聘信息直接删除
3.将薪资分为底薪和上限两列。一律转换为xxx元/月,保留纯整数。
将薪资缺失的招聘信息直接删除。
4.将发布时间变为日期时间型格式,形如年-月-日格式。
5.将“学历要求”转换为如下6个类别:
无 初中及以下 高中/中技/中专 大专 本科 硕士 博士
如果有缺失,则用“无”填充
6.招聘人数:转换为整型;
如果为 缺失或“若干人”,都用“1”填充或替换;
7.工作年限要求:转换为整型;如果为范围,则取最小值。如3-5年,就取3;如无经验要求或缺失,用“0”表示。
8.职能类别:如果含多个职能类别,如大数据开发/分析,就取第1个。
import re import pandas as pd from numpy import NaN df =pd.read_csv("WuyouBigdata.csv") #print(df) # 1.规范岗位名称 # 有的岗位名称含有()或者()或者- 或者空格 或者/,补充说明地点,背景方向要求,福利,学历等信息。把岗位名称中的补充信息全部去掉。 df = df[~df['work_name'].str.contains('-')]#~取反 df = df[~df['work_name'].str.contains('/')] df = df[~df['work_name'].str.contains('\(')] df = df[~df['work_name'].str.contains('(')] # 2.从工作地点中提取出城市名 # 将含有“异地招聘”字样或工作地点缺失的招聘信息直接删除 df = df[~df['work_location'].str.contains('异地招聘')] df=df.dropna(subset=['work_location']) df = df[~df['work_location'].str.contains('空')] df = df.reset_index().drop('index', axis=1)#清洗 链接起来去空的数据 # 提取城市名 df['work_location'] = [i.split('-')[0] for i in df['work_location']] # 3.将薪资分为底薪和上限两列。一律转换为xxx元/月,保留纯整数。 # 将薪资缺失的招聘信息直接删除 salary_min=[] salary_max=[] df= df.dropna(axis=0, how='any') # 将薪资缺失的招聘信息直接删除 for salary in df['work_salary']: split_data = salary.split('-') salary_min.append(float(split_data[0])) if '万' in split_data[1]: salary_max.append(str(float(split_data[1].split('万')[0])*10000)+'元/月') elif '千' in split_data[1]: salary_max.append(str(float(split_data[1].split('千')[0])*1000)+'元/月') df['salary_min'] = salary_min df['salary_max'] = salary_max # 4.将发布时间变为日期时间型格式,形如年-月-日格式。 df['release_date'] = ['2019-'+str(i) for i in df['release_date']] df['release_date'] = [pd.to_datetime(i, format="%Y-%m-%d") for i in df['release_date']] # 5.将“学历要求”转换为如下6个类别: # 无 初中及以下 高中/中技/中专 大专 本科 硕士 博士 # 数据有问题,该题不做 # 6.招聘人数:转换为整型; # 如果为 缺失或“若干人”,都用“1”填充或替换; people_list=[] for i in df['work_require_people']: count = re.findall('招(.*?)人',i) if '若干' in count[0]: people_list.append(1) else: people_list.append(int(count[0])) df['work_require_people'] = people_list # 7.工作年限要求:转换为整型;如果为范围,则取最小值。如3-5年,就取3;如无经验要求或缺失,用“0”表示。 exp_list = [] for i in df['work_exp']: '3-4年经验' split_data = i.split('-') if len(split_data)>1: exp_list.append(int(split_data[1].split('年')[0])) else: if '无' in split_data[0]: exp_list.append(0) else: exp_list.append(int(split_data[0].split('年')[0])) df['work_exp'] = exp_list # 8.职能类别:如果含多个职能类别,如大数据开发/分析,就取第1个。 # 数据原因(没有该列数据) 此题不做 # 将数据保存到新的csv文件 df.to_csv('new_bigdata.csv',index=False)
三、数据分析与可视化
代码在题目下方 (注意:代码内的部分内容可能与上面爬虫所爬取的数据不同(如字段、文件名),请根据实际情况修改)
1.统计各个城市的大数据相关岗位的招聘人数,将人数排名前10的用柱状图展示。

2.统计大数据各个岗位的招聘人数,用柱状图展示最受欢迎(需求量大)的十大岗位。

3.统计各地平均底薪薪资,将排名前三和后三的用合适的图表展示。

4.统计各种学历要求所占的比例,用饼状图展示。

6.统计各种公司性质(事业或私营等)所需大数据相关岗位的人数,用柱状图显示。

7.统计每种职能类别下每种学历要求对应的岗位数量。
【比如:对于大数据开发这种职能类别,分别统计要求博士,硕士等的岗位分别有多少。】
8.统计每个城市中每种学历需求对应的岗位数量。
9.统计每个城市中每种岗位或职能类别的平均薪资(底薪,上限,平均都行)。
说明:数据分析与可视化部分完成5个即可。多多益善。
import matplotlib.pyplot as plt import pandas as pd # 解决中文显示问题 font = {'family' : 'SimHei', 'weight' : 'bold', 'size' : '12'} plt.rc('font', **font) # 步骤一(设置字体的更多属性) plt.rc('axes', unicode_minus=False) # 步骤二(解决坐标轴负数的负号显示问题) # 读取数据 data = pd.read_csv('new_bigdata.csv') def question1(): # 1.统计各个城市的大数据相关岗位的招聘人数,将人数排名前10的用柱状图展示。 # 根据城市分组求和 降序排序 new_data = data.groupby('work_location')['work_require_people'].sum().sort_values(ascending=False) question1_result_list = new_data[:10] # 画图 x = [i for i in range(len(question1_result_list))] y = question1_result_list plt.bar(x,y,tick_label=list(question1_result_list.index)) plt.title('招聘人数排名前10的城市') for i,j in zip(x,y): plt.text(i,j,'{}人'.format(j)) plt.show() def question2(): # 2.统计大数据各个岗位的招聘人数,用柱状图展示最受欢迎(需求量大)的十大岗位。 # 由于数据原因 此处根据职位名分组 new_data = data.groupby('work_name')['work_name'].count().sort_values(ascending=False) question2_result_list = new_data[:10] # 画图 x = [i for i in range(len(question2_result_list))] y = question2_result_list # 解决显示重叠问题 label = [i[:3]+'\n'+i[3:-2]+'\n'+i[-2:] for i in list(question2_result_list.index)] plt.bar(x,y,tick_label=label) plt.title('最受欢迎(需求量大)的十大岗位') for i,j in zip(x,y): plt.text(i,j,'{}人'.format(j)) plt.show() # 3.统计各地平均底薪薪资,将排名前三和后三的用合适的图表展示。 def question3(): # 将薪资后面的“元/每月”去掉 salary_min = [] for i in data['salary_min']: try: salary_min.append(float(i[:-3])) except: salary_min.append(float(data['salary_min'][0][:-3])) #salary_min = [float(i[:-3]) for i in data['salary_min']] data['salary_min'] = salary_min # 根据城市分组,对薪资求平均值 new_data = data.groupby('work_location')['salary_min'].mean().sort_values(ascending=False) head3_data = new_data[:3] tail3_data = new_data[-3:] # 将两个数据在一个柱状图上展示 用图例区分 x1 = [0,1,2] x2 = [3,4,5] y1 = head3_data y2 = tail3_data label = list(head3_data.index) [label.append(i) for i in (list(tail3_data.index))] plt.title('平均底薪排名前三和后三的城市') plt.bar(x1,y1,) plt.bar(x2,y2,) plt.xticks([i for i in range(6)],label) plt.legend(['前三名','后三名']) plt.show() # 4.统计各种学历要求所占的比例,用饼状图展示。 def question4(): # 根据学历分组,统计总数 new_data = data.groupby('edu_level')['edu_level'].count() labels = list(new_data.index) sizes = [i for i in new_data] print(labels,sizes) fig1, ax1 = plt.subplots() ax1.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90) ax1.axis('equal') # 等长径比确保饼图被画成圆形。 ax1.set_title('各种学历要求所占的比例') plt.show() # 6.统计各种公司性质(事业或私营等)所需大数据相关岗位的人数,用柱状图显示。 def question6(): # 根据公司性质分组,对招聘人数求和 new_data = data.groupby('company_type')['work_require_people'].sum().sort_values(ascending=False) # 画图 x = [i for i in range(len(new_data))] y = new_data plt.bar(x, y, tick_label=[i[:2]+'\n'+i[2:] for i in list(new_data.index)]) plt.title('各种公司性质(事业或私营等)所需大数据相关岗位的人数') for i, j in zip(x, y): plt.text(i, j, '{}人'.format(j)) plt.show() # 7.统计每种职能类别下每种学历要求对应的岗位数量。 # 【比如:对于大数据开发这种职能类别,分别统计要求博士,硕士等的岗位分别有多少 def question7(): # 数据原因 对职位名称进行分组,再对学历进行分组,求和 new_data = data.groupby(['work_name','edu_level'])['work_require_people'].sum() print(new_data,type(new_data)) # 8.统计每个城市中每种学历需求对应的岗位数量。 def question8(): # 对城市、学历分组 对需求求和 new_data = data.groupby(['work_location','edu_level'])['work_require_people'].sum() print(new_data) # 9.统计每个城市中每种岗位或职能类别的平均薪资(底薪,上限,平均都行)。 def question9(): # 将薪资后面的“元/每月”去掉 try: salary_min = [float(i[:-3]) for i in data['salary_min']] except: salary_min = [i for i in data['salary_min']] data['salary_min'] = salary_min # 对城市、岗位进行分组 求底薪平均值 new_data = data.groupby(['work_location', 'work_name'])['salary_min'].mean() print(new_data) if __name__ == '__main__': question1() question2() question3() question4() question6() question7() question8() question9()