一、数据爬取
1.1 数据库创建
首先,打开数据库,右键单击计算机用户新建数据库。
右键点击表,在数据库中新建表‘job’ ,用于存放爬取的岗位信息,字段定义如下图。
1.2 python爬虫代码
第二步,进入爬取阶段。本实验对ChromeDriver重新进行版本配置后,依次对信管专业未来可能从事的30个岗位进行数据爬取。现以“数据分析岗位”的爬取过程为例,更改后的代码如下所示。
在爬取每个岗位的数据之前,首先需要通过观察确定爬取该岗位的页数。即
需要先自行通过 51job 首页关键词搜索栏搜索每个岗位的数据信息,观察每种岗
位大概有多少页数据,然后一个一个岗位进行爬取。
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from bs4 import BeautifulSoup
import pymysql
#定义 jiexi()函数,用于解析得到的 html
def jiexi(html, info, name):
soup = BeautifulSoup(html, "lxml")
text = soup.find_all("script", type="text/javascript")#[2].string
text=str(text).split('window.__SEARCH_RESULT__')[1]
text=text.split("</script>, ")[0]
# print('————————————————————————————————————————')
# print(text)
#观察原始代码发现我们需要的数据在 engine_jds 后
data = eval(text.split("=", 1)[1])["engine_jds"]
for d in data:
try:
job_name = d["job_name"].replace("\\", "") # 岗位名称
except:
job_name = " "
try:
company_name = d["company_name"].replace("\\", "")#公司名称
except:
company_name = " "
try:
providesalary_text=d["providesalary_text"].replace("\\", "") #薪资
except:
providesalary_text = " "
try:
workarea_text = d["workarea_text"].replace("\\", "") #工作地点
except:
workarea_text = " "
try:
updatedate = d["updatedate"].replace("\\", "") #更新时间
except:
updatedate = " "
try:
jobwelf = d["jobwelf"].replace("\\", "") # 工作待遇
except:
jobwelf = " "
try:
companyind_text=d["companyind_text"].replace("\\", "") #公司类型
except:
companyind_text = " "
try:
companysize_text = d["companysize_text"].replace("\\", "") #公司规模
except:
companysize_text = " "
try:
at = d["attribute_text"] # 工作要求
s = ''
for i in range(0, len(at)):
s = s + at[i] + ','
attribute_text = s[:-1]
except:
attribute_text = " "
#将每一条岗位数据爬取下的内容以及传入参数 name 作为一个列表,依此加入到info 列表中
info.append( [name, job_name, updatedate, company_name,
companyind_text, companysize_text, workarea_text, providesalary_text,
attribute_text, jobwelf])
#将数据存到 MySQL 中名为“51job”的数据库中
def save(data):
db = pymysql.connect( #连接数据库
host="127.0.0.1", #MySQL 服务器名
user="root", #用户名
password="root", #密码
database="Python 上机", #操作的数据库名称
charset="utf8"
)
cursor = db.cursor()
#将数据保存到数据库表中对应的列
for each_data in data:
present_job = each_data[0] #当前爬取岗位
job_name = each_data[1] #岗位
updatedate = each_data[2] #更新时间
company_name = each_data[3] #公司名称
companyind_text = each_data[4] #公司类型
companysize_text = each_data[5] #公司规模
workarea_text = each_data[6] #工作地点
providesalary_text = each_data[7] #薪资
attribute_text = each_data[8] #工作要求
jobwelf = each_data[9] #工作待遇
#创建 sql 语句
sql = "insert into jobs(当前爬取岗位, 岗位, 更新时间,公司名称,公司类型,公司规模,工作地点,薪资,工作要求,工作待遇)
values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
#执行 sql 语句
cursor.execute(sql, [present_job, job_name, updatedate, company_name, companyind_text, companysize_text, workarea_text, providesalary_text, attribute_text, jobwelf])
db.commit() #提交数据库
cursor.close() # 关闭游标
db.close() # 关闭数据库
if __name__ == '__main__': #主函数
# searchword = input('请输入你想查询的岗位:') #自行输入 30 个岗位名称搜索
# pages = int(input('请输入需要爬取的总页数:')) #在观察后输入数据页面爬取数据
searchword = '产品运营' # 自行输入 30 个岗位名称搜索
pages = 30 # 在观察后输入数据页面爬取数据
mainurl = 'https://www.51job.com' #设置 51jobs 首页的url
option = webdriver.ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enableautomation'])
option.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(options=option)
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
browser.get(mainurl) #打开 51jobs 首页
browser.implicitly_wait(10)
browser.find_element(by=By.XPATH,
value='/html/body/div[3]/div/div[1]/div/div/p[1]/input').send_keys(searchword) #在搜索框输入要查询的岗位
browser.implicitly_wait(10)
button1 = browser.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[1]/div/button') #寻找搜索按钮
button1.click() #点击搜索按钮
time.sleep(10)
windows = browser.window_handles
browser.switch_to.window(windows[-1])
info = [] #空列表,传入 jiexi 函数用于存储每一条岗位的数据
for i in range(1, pages+1):
html = browser.page_source #获取网页的 html
# print('————————————————————————————————————————')
# print(html)
browser.implicitly_wait(10)
jiexi(html, info,searchword)
if i<=5:
button2 =
browser.find_element(By.XPATH,value='/html/body/div[2]/div[3]/div/div
[2]/div[4]/div[2]/div/div/div/ul/li[{}]/a'.format(i+7))
else:
button2=browser.find_element(By.XPATH,value='/html/body/div[2]/div[3]/div/div[2]/div[4]/div[2]/div/div/div/ul/li[13]/a') #寻找下一页的按钮
button2.click() #翻到下一页
time.sleep(10)
windows = browser.window_handles
browser.switch_to.window(windows[-1])
save(info)
print('{}岗位的{}页数据已经爬取成功!请进入 Mysql 数据库检查'.format(searchword,pages))
1.3 爬取结果
结果如下图。
二、数据清洗
2.1 关键词匹配工作岗位
在爬取数据前,人工查看每个职位的“关键词”所涵盖的页数,并按事先定好的页数爬取,但仍无法避免一些无关的招聘信息被爬取,这里采用压缩包中给出的匹配代码进行岗位匹配,匹配后较之前减少372条数据。
def pipei():
results =all_case_info# 接受全部的返回结果
after_pipei = [] # 建立一个空列表,用来存储匹配后数据
for each_result in results:
if each_result[-1] == '物流与供应链':
if '物流' in each_result[0] or '供应链' in each_result[0]:
after_pipei.append(each_result)
elif each_result[-1] == '新媒体运营' or each_result[-1] == '电商运营':
if '运营' in each_result[0]:
after_pipei.append(each_result) # 由于在 以关键词“电商运营”或“新媒体运营”搜索的岗位信息中包含大量具体电商或 新媒体平台名称的岗位名称,如“拼多多运营”“抖音运营”等,因此在这两类 岗位名称匹配时我们认为只要岗位名称中包含“运营”就算匹配成功。
elif each_result[-1] == '客户关系管理':
if '客户关系' in each_result[0]:
after_pipei.append(each_result)
elif each_result[-1] == '安卓开发':
if '安卓' in each_result[0] or 'Android' in each_result[0]:
after_pipei.append(each_result) # 由于在 很多公司的招聘岗位中“安卓”会以“Android”英文形式出现,因此,在以“安 卓开发”为关键词进行搜索时,我们认为只要包含“安卓”或“Android”开发 就算匹配成功。
elif each_result[-1][:-2] in each_result[0] and each_result[- 1][-2:] in each_result[0]:after_pipei.append(each_result) # 剩余岗位需 要两个关键词都存在岗位名称中,例如包含“数据”或“分析”在以“数据分析” 为关键词搜索的岗位名称种,我们就认为匹配成功。
return after_pipei # 返回匹配后的列表
if __name__ == '__main__':
workbook = xlrd2.open_workbook('/Users/xxx/总表.xlsx')
sheet = workbook.sheet_by_index(0)
# 双列表形式 ,一行一个用例
all_case_info = []
for i in range(1, sheet.nrows):
case_info = []
for j in range(sheet.ncols):
case_info.append(sheet.cell_value(i, j))
all_case_info.append(case_info) # 注意,python以对齐来确定循环的所定义区域
data=pipei()
print(data[0])
print(len(data))
#print(data)
book = xlwt.Workbook(encoding='utf-8', style_compression=0)
sheet = book.add_sheet('匹配后数据', cell_overwrite_ok=True)
col = ('岗位', '更新时间', '公司名称', '公司属性', '公司规模', '工作地点', '薪资', '经验要求', '学历要求', '工作待遇', '当前爬取岗位')
for i in range(0, 11):
sheet.write(0, i, col[i])
for i in range(0, 12260):
a = data[i]
for j in range(0, 11):
sheet.write(i + 1, j, a[j])
savepath = '/Users/xxx/匹配表格.xls'
book.save(savepath)
2.2 清洗“薪资”数据
原始数据中的薪资单位较为杂乱,有以“年”为单位的,还有以“月”为单位的,个别招聘信息还标明了“13/14薪”。为了后续可视化分析工作的顺利进行,本报告将按照要求将单位统一为“千/月”,该部分所使用的代码如高亮部分所示(在给出代码的基础上略微修改),清洗前后的对比如下图所示。
def salary_1(data): #清除工作薪资内容的“·xx 薪”
after_salary_1=[]
for each_data in data:
if '千' in each_data[6] and each_data[6][-1] == '万':
each_data[6]=str((float(each_data[6].split('千')[0]) / 10))+each_data[6].split('千')[1]
if '薪'in each_data[6]:
each_data[6]=each_data[6].split('·')[0]
after_salary_1.append(each_data)
return after_salary_1
def salary(data):
after_salary = [] # 建立一个空列表,用来存储匹配后数据
for each_data in data:
if each_data[6] != '': # 筛除缺失值,以小时 计费,给出的薪资表达为在“……以下”及“……以上”等难以计算数据的工作岗位#统一量纲(单位:千/月)
if each_data[6][-1] == '年':
try:
each_data[6] = str((float(each_data[6].split('万')[0].split('-')[0]) + float(each_data[6].split('万')[0].split('-')[1])) * 5 / 12) + '千/月'
except:
each_data[6] = each_data[6]
elif each_data[6][-1] == '天':
try:
each_data[6] = str(float(each_data[6].split('元')[0]) * 30 / 1000) + '千/月'
except:
each_data[6] = each_data[6]
elif each_data[6][-1] == '万':
try:
each_data[6] = str((float(each_data[6].split('万')[0].split('-')[0]) + float(each_data[6].split('万')[0].split('-')[1])) * 5) + '千/月'
except:
each_data[6] = each_data[6]
else:
try:
each_data[6] = str((float(each_data[6].split('千')[0].split('-')[0]) + float(each_data[6].split('千')[0].split('-')[1])) / 2) + '千/月'
except:
each_data[6] = each_data[6]
after_salary.append(each_data)
# print(after_salary)
return after_salary # 返回平均工资后的数据
2.3 工作地点清洗
仅保留工作地点中的城市,对“-”之后的内容进行删减,代码如高亮部分所示,清洗后的结果如下图所示。
def split_city(data):
after_split_city = [] # 建立一个空列表,用来存储匹配后数据
for each_date in data:
each_date_list = list(each_date)
each_date_list[5] = each_date_list[5].split('-')[0] # 将数据表中工作地点列以'-'进行切割,选取第一个元素替换
after_split_city.append(each_date_list)
return after_split_city # 返回筛除后的数据
2.4 工作要求分列
我们从爬取成功的数据中可以看到“工作要求”一列中有经验要求和学历要求两大类,两者之间用“|”隔开。本报告将数据导出为excel,并使用Excel中的“分列”功能将“工作要求”分成“经验要求”和“学历要求”,如图所示。
三、数据可视化
3.1 柱状图
对清洗后的数据按照信管专业未来岗位的九小类进行统计汇总,以柱状图的形式来表示可以直观的观察出九个类别中的热门职业类别,代码如下高亮部分所示,岗位类别柱状图如图所示。
def gangweishuliang_hist(): #主函数调用的 gangweishuliang_hist ()函数
#选出 30 个岗位关键词所在的列数据
results = cols #col为上文读取的全部数据,以列表形式存储
jobs = ['产品经理', '产品助理', '交互设计', '前端开发', '软件设计', 'IOS开发', '业务分析', '安卓开发', 'PHP开发', '业务咨询','需求分析', '流程设计', '售后经理','售前经理', '技术支持', 'ERP实施', '实施工程师', 'IT项目经理', 'IT项目助理', '信息咨询','数据挖掘', '数据运营', '数据分析', '网络营销', '物流与供应链', '渠道管理', '电商运营', '客户关系管理', '新媒体运营', '产品运营']
count = [] #创建一个空列表,用于存储每种岗位的数量值
for i in range(len(jobs)):
count.append(0) #先在空列表中创建 30 个 0 元素
for each_result in results:
for i in range(0, 30):
if each_result == jobs[i]:
count[i] += 1
continue #计算每种岗位的数量
jobs_classification = ['技术管理类', 'IT 运维类', '技术开发类', '业务咨询类','技术支持类', '数据运营类', '市场职能类', '产品运营类','数据管理类'] #将 30 种岗位划分为九小类
counts = [] #创建一个空列表,用于存储每小类岗位的数量值
for i in range(len(jobs_classification)):
counts.append(0) #先在空列表中创建 9 个 0 元素
#根据大纲中给出的分类表,依据 30 种岗位数量值,分别计算出九小类岗位的数量值
counts[0] = count[0] + count[1] + count[2]
counts[1] = count[17] + count[18]
counts[2] = count[3] + count[4] + count[5] + count[6] + count[7] + count[8]
counts[3] = count[9] + count[10] + count[11]
counts[4] = count[12] + count[13] + count[14] + count[15] + count[16]
counts[5] = count[21] + count[22]
counts[6] = count[23] + count[24] + count[25]
counts[7] = count[26] + count[27] + count[28] + count[29]
counts[8] = count[19] + count[20]
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
#画柱状图
fig, ax = plt.subplots(figsize=(10, 7)) #定义图片的大小
ax.bar(x=jobs_classification, height=counts, color='lightblue')
ax.set_title("岗位数量柱状图", fontsize=15) #为柱状图命名并设置字体大小
for x, y in enumerate(counts):
plt.text(x, y + 5, '%s' % y, ha='center') #为每根柱子加上数值
plt.show() #展示图片
if __name__ == '__main__':
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
由图4可知,在最近的一段时间里(12月16日之前的两周内),信管专业的招聘岗位主要以技术开发、技术管理、技术支持以及产品运营为主。按大类看,技术设计与实现的岗位较多,是目前信管毕业生最大的就业去向,其对应的热门岗位为产品经理、前端开发、业务分析等。同时,技术支持和产品运营发展迅速,这些岗位对技术的要求较技术开发和技术管理低,但就业市场的需求量和人才缺口都较大,适合偏向管理方向的信管毕业生。
整体来看,信管的就业市场还是强调“技术”,这也是我国信管专业与国外最大的不同。从图4可推知,“管理”岗位将在不久的将来代替“技术”岗位成为信管专业的主要从业方向。
3.2 饼状图
3.2.1 公司属性饼状图
本文采集的数据与案例稍有不同:本文采集了与信管专业相关的用人单位的公司属性(民营/国营/外资)。下面将分析与信管专业相关的用人单位是民企、国企、外资企业的相对比例。
代码如下,公司属性由图所示。
import xlrd2
import matplotlib.cm as cm
import matplotlib.pyplot as plt #导入绘图包
def shuxing_pie(a): #主函数调用的 shuxing_pie()函数
results = a
#shuxing = [] #创建一个空列表,用来装工作要求种的“学历“数据
after_quchong_shuxing=['民营','国企','外资(欧美)','外资(非欧美)','合资']
counts = [] #创建一个空列表,用于装所有岗位信息中每一种学历的数量值
for i in range(len(after_quchong_shuxing)):
counts.append(0)
for each in results:
for i in range(len(after_quchong_shuxing)):
if each == after_quchong_shuxing[i]:
counts[i]+=1 #计算每种学历的数量值
#在画图前,先设置参数 explode,计算每个城市数据分析岗位占比,让占比小于 5%的城市在饼图中突出一部分
A = []
for i in range(0, len(after_quchong_shuxing)):
A.append(0)
for i in range(0,len(after_quchong_shuxing)):
total = sum(counts[:len(after_quchong_shuxing)])
if (counts[i]/total) < 0.05:
A[i] = 0.2 #让占比小于 5%的城市突出
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
colors = ['lightblue', 'Coral','Turquoise','DeepPink','LightGoldenrodYellow']
plt.pie(x=counts, explode=A, labels=after_quchong_shuxing,autopct='%1.2f%%',colors=colors) # x 为基本数据,labels 为给各个部分添加标签的列表,autopct 显示各部分比例,本例中调用%1.2f%%。
plt.title('公司属性饼状图') #为饼图添加标题
plt.show() #展示图片
if __name__ == '__main__':
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
rows = sheet.nrows # 获取行数
cols = sheet.ncols # 获取列数,尽管没用到
all_content = []
cols = sheet.col_values(3)
print(cols)
shuxing_pie(cols)
由上图我们可以看出,信管专业的相关用人单位的属性类型是有一定规律:
有90%的相关用人单位都是民营,也就是说信管专业有90%的从业者都在民营企业工作。近年来,我国互联网行业迅速发展,不少互联网企业,如阿里、字节跳动、腾讯、网易等,成立于本世纪初,并在最近几年取得辉煌的成绩。得益于互联网企业的高速发展,信管专业的很多岗位都来源于我国强大的民营互联网公司。
信管专业毕业生进入国企的机会相对较少。目前就业市场上仅有不到1%的信管岗位是国企提供的,这表现出我国国有企业对信息系统的重视程度不高,对相关人才的需求较少。这启发信管专业想要进入国企工作的同学要做多手准备。
信管专业的毕业生在外资与合资企业的人数相对较少,均不足5%,其中外资企业大多是欧美国家的公司。这与国内近年来互联网企业的高速发展、高薪水、本土化有关。
3.2.2 学历要求饼状图
def xueli_pie(): #主函数调用的 xueli_pie()函数
cursor = db.cursor()
cursor.execute("select `工作要求` from after_clean") #选出工作要求的列数据
results = cursor.fetchall()
xueli = [] #创建一个空列表,用来装工作要求种的“学历“数据
for each_result in results:
if each_result[0] !='' and each_result[0].split(',')[1]!='':
xueli.append(each_result[0].split(',')[1])
after_quchong_xueli = list(set(xueli)) #对学历去重复值,由于元组不能更改,转换为列表类型,便于后续操作
after_quchong_xueli = ['在校生/应届生' if each == '在校生\\/应届生' else each for each in after_quchong_xueli]
counts = [] #创建一个空列表,用于装所有岗位信息中每一种学历的数量值
for i in range(len(after_quchong_xueli)):
counts.append(0)
for each in xueli:
for i in range(len(after_quchong_xueli)):
if each == after_quchong_xueli[i]:
counts[i]+=1 #计算每种学历的数量值
#在画图前,先设置参数 explode,计算每个城市数据分析岗位占比,让占比小于 5%的城市在饼图中突出一部分
A = []
for i in range(0, len(after_quchong_xueli)):
A.append(0)
for i in range(0,len(after_quchong_xueli)):
total = sum(counts[:len(after_quchong_xueli)])
if (counts[i]/total) < 0.05:
A[i] = 0.2 #让占比小于 5%的城市突出
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
plt.pie(x=counts, explode=A, labels=after_quchong_xueli, autopct='%1.2f%%',pctdistance=0.9,explode=(0,0.2,0.6,0,0.2,0.6,0.9)) # x 为基本数据,labels 为给各个部分添加标签的列表,autopct 显示各部分比例,本例中调用%1.2f%%。
plt.title('学历要求饼状图') #为饼图添加标题
plt.show() #展示图片
cursor.close() #关闭操作游标
由上图可知:
信管专业相关的岗位对学历有较高的要求。对本科生的需求达到52.6%,超过了一半,对大专生的需求次之,达到42%,这两类占了信管就业要求的绝大部分。
高学历如硕士、博士的需求较少,这与硕博数量较少、硕博更加专注于科研有关。信管相关的岗位还是有着较高的专业门槛,对低学历的需求极少。
综上我们得出的结论是:信管专业的本科生就业形势较好,对硕士、博士等高学历需求不大。如果目标是就业的话,本科学历就可以帮助我们找到较好的工作。
3.3 条形图
3.3.1 公司规模条形图
import xlrd2
import matplotlib.pyplot as plt
def company_size(a): # 主函数调用的 company_size()函数
results = a
# 筛出非空数据,装进 size 列表中
size = []
for each_result in results:
if each_result != '':
size.append(each_result)
# 将公司规模类型去重复值,得到例如['少于 50 人', '1000-5000 人', '5000-10000 人', '50-150 人', '150-500 人', '10000 人以上', '500-1000 人']的after_quchong 列表
after_quchong = list(set(size))
# 计算公司规模的类型数量 type_size
type_size = len(after_quchong)
# 计算每种公司规模的岗位数
# 创建一个起始值为 0,有 type_size 各元素的 count_each_size 列表
count_each_size = []
for i in range(type_size):
count_each_size.append(0)
# 计算不同规模公司的岗位数量
for each_size in size:
for each in range(0, type_size):
if each_size == after_quchong[each]:
count_each_size[each] = count_each_size[each] + 1
# 创建一个公司规模类型和数量一一对应的字典
dic = {}
for i in range(0, type_size):
dic[count_each_size[i]] = after_quchong[i]
# 本例希望画出经过排序后的图形,由于 sort 排序会直接改变列表原来的元素,因此本例先新建一个与 count_each_size 相同的 order 列表,再进行排序
order = count_each_size
order.sort()
scale = []
for i in range(0, type_size):
scale.append(dic.get(count_each_size[i])) # 通过字典,用新建的order 列表找到对应的公司规模类型,生成新的 scale 列表,此时两个列表中的元素是一一对应关系
# 画图
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.barh(scale, count_each_size,color='LightSalmon') # 横放条形图函数 barh
plt.xlabel('数量') # x 轴名称
plt.ylabel('规模') # y 轴名称
plt.title('公司规模') # 图形名称
for y, x in enumerate(count_each_size):
plt.text(x + 0.1, y, "%s" % round(x, 1), va='center') # 为每个柱子加上对应的数值,其中 round(x,1)是将 x 值四舍五入到一个小数位
plt.savefig( '/Users/xiechongyang/Desktop/大三上/python数据爬虫及可视化实验/条形1.png')
plt.show() # 展示图形
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
#print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
rows = sheet.nrows # 获取行数
cols = sheet.ncols # 获取列数,尽管没用到
cols = sheet.col_values(4)
company_size(cols)
由上图可知:
本报告所爬取的信管专业的岗位大多来自中等规模的企业(50-500人),即不想要进入超级企业、渴望进入中小型企业工作的信管毕业生可以通过“前程无忧51jobs”网站获取招聘资讯。同时,该图也反映出目前进入“大厂”工作的机会减少,中小型互联网企业发展势头较好,从中小企业获得满意offer的几率较大。我们可以多关注近些年发展比较好的中小型企业。
3.3.2 城市-岗位数条形图
import xlrd2
import matplotlib.pyplot as plt
import numpy as np
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
cols = sheet.col_values(5)
print(cols)
city=['北京','上海','广州','深圳','南京','武汉','长沙','杭州','成都','厦门','天津','重庆'] #13个城市
count=[0,0,0,0,0,0,0,0,0,0,0,0]
for i in cols:
for j in range(len(city)):
if i==city[j]:
count[j]+=1
#print(count)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 输入统计数据
x_lable = city
bar_width = 0.2 # 条形宽度
index_x_train = np.arange(len(x_lable)) # 训练集条形图的横坐标
# 使用bar 函数画出两组条形图
plt.bar(index_x_train, height=count, width=bar_width, color='LightSeaGreen', label='岗位数')
plt.legend(loc="upper right", prop={"size": 12, }) # 显示图例 设置图例的大小和方向
plt.yticks(fontsize=16, color='#000000') # 改变纵坐标刻度的大小
plt.xticks(index_x_train + bar_width / 2, x_lable, size=16,rotation=70) # 让横坐标轴刻度显示 x_lable中的动作类型, index_x_train + bar_width/2 为横坐标轴刻度的位置
# plt.ylabel(' ') # 纵坐标轴标题
# plt.title(' ') # 图形标题
plt.show()
由上图可知:
北上广深等超一线城市在相关岗位招聘数量上占绝对优势,比新一线城市南京、武汉、成都、长沙等高很多。信管专业毕业生的工作城市可以选择上图所示的主要城市,在这些城市的发展机会更多。接下来将分析上述城市在4大类岗位上的招聘情况。
3.3.3 岗位-城市分组条形图
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
cols = sheet.col_values(-1)
chengshi=sheet.col_values(5)
dalei=[]
city=['北京','上海','广州','深圳','南京','武汉','长沙','杭州','成都','天津','重庆'] #12个城市
T=["产品经理","产品助理","交互设计","前端开发","软件设计","IOS开发","业务分析","安卓开发","PHP开发"]
C=["业务咨询","需求分析","流程设计","售后经理","售前经理","技术支持","ERP实施","实施工程师","IT项目经理","IT项目助理"]
D=["信息咨询","数据挖掘","数字运营","数据分析"]
P=["网络营销","物流与供应链","渠道管理","电商运营","客户关系管理","新媒体运营","产品运营"]
T_num=[0,0,0,0,0,0,0,0,0,0,0]
C_num=[0,0,0,0,0,0,0,0,0,0,0]
D_num=[0,0,0,0,0,0,0,0,0,0,0]
P_num=[0,0,0,0,0,0,0,0,0,0,0]
for i in cols:
if i in T:
dalei.append("技术设计与实现")
if i in C:
dalei.append("IT咨询与运维")
if i in D:
dalei.append("数据管理决策")
if i in P:
dalei.append("产品与服务管理")
print(len(dalei))
dalei_sel=[]
city_sel=[]
for i in range(len(cols)):
if chengshi[i] in city:
city_sel.append(chengshi[i])
dalei_sel.append(dalei[i])
print(len(city_sel))
print(len(dalei_sel))
#city=['北京','上海','广州','深圳','南京','武汉','长沙','杭州','成都','厦门','天津','重庆'] #12个城市
for i in range(len(chengshi)):
for j in range(len(city)):
if chengshi[i]==city[j]:
if dalei[i]=='技术设计与实现':
T_num[j]+=1
elif dalei[i]=='IT咨询与运维':
C_num[j]+=1
elif dalei[i]=='数据管理决策':
D_num[j]+=1
else:
P_num[j] += 1
labels=city
avg=[]
for i in range(len(T_num)):
a=(T_num[i]+C_num[i]+D_num[i]+P_num[i])/4
avg.append(a)
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
# Figure 1
plt.figure()
width=0.16
plt.bar(x-width*2, T_num, width, label='技术设计与实现',color='NavajoWhite')
plt.bar(x -width, C_num, width, label='IT咨询与运维',color='LightSkyBlue')
plt.bar(x, D_num, width, label='数据管理决策',color='CornflowerBlue')
plt.bar(x +width, P_num, width, label='产品与服务管理',color='LightPink')
plt.bar(x +width*2, avg, width, label='四类平均',color='Lavender')
# Add some text for labels, title and
# custom x-axis tick labels, etc.
plt.ylabel('岗位数量')
plt.title('各大主要城市分类招聘条形图')
plt.xticks(np.arange(11), city,rotation=70)
plt.legend()
plt.show()
由上图可知:
对于信管专业,上述11座城市招聘最多的几乎都是技术设计与实现岗位,其中上海、深圳的相关岗位最多,广州、北京紧随其后,在新一线城市中,杭州的发展潜力最大。鉴于此,未来想要从事技术岗的同学可以考虑上海、深圳、杭州等地。
IT咨询与运维的岗位招聘数量仅次于技术设计与实现,第三大类就是产品与服务管理,数据管理与决策的岗位相对其他三类是最少的。IT咨询与运维的分布趋势同技术设计与实现类似。北京的产品与服务管理岗位相对与其他超级城市较少,与新一线城市的水平相近,对于未来想要从事产品与服务管理的同学可以考虑东部沿海城市。
4大超级城市的数据管理决策岗位招聘情况相近,差别不大;新一线城市之间的区别也不大。对于工作地点,将来要从事数据管理、分析相关岗位的同学可以选择的城市较多,因为该行业为表现出强烈的城市/地区差异。
总体来看,在四大超级城市中,这四大类岗位的平均招聘人数也呈现出东南沿海强于内地的趋势。
3.4 热力图
def area_operation():
path = os.path.join(data_path, file_name[1])
# print(path)
test_data = pd.read_csv(path, delimiter="#", encoding='utf-8', dtype={'salary': str})
area=test_data['area']
#print(area)
final=[]
for format in area.iteritems():
kkk=re.split(r'-',format[1])
final.append(kkk[0])
series=pd.Series(final)
area_count=series.value_counts()
data_sheng=[]
data_shi=[]
for k in area_count.iteritems():
print(k[0])
name=k[0]
print(type(k[0]))
if name in ['北京','上海','天津','重庆','陕西省','湖北省','江苏省','广东省']:
name=re.sub(r'[省]','',name)
tmp=[name,k[1]]
data_sheng.append(tmp)
else:
name = name + '市'
tmp=[name,k[1]]
data_shi.append(tmp)
print(data_sheng)
print(data_shi)
c = (
Geo()
.add_schema(maptype="china")
.add("省",data_sheng)
.add('市',data_shi)
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(),
title_opts=opts.TitleOpts(title="工作分布"),
)
)
c.render('./view/area_heatMap.html')
由上图可知:
前程无忧51jobs的信管专业相关岗位主要分布在以上海为中心的东部沿海地区、以广州深圳为中心的南部沿海地区、京津冀、以武汉为中心的华中地区。岗位分布热力图为我们未来就业提供了工作地点的建议,由于以上地区岗位多、经济发达,对于我们来说更有专业发展和提升的空间,同时这些地区的岗位较多,留给我们的选择空间较大。
北上广深的相关岗位最多,武汉、成都、长沙次之,对于不希望未来工作太“卷”的同学可以将目光放在武汉、成都等较为发达的省会城市,这些地方的压力相较于超一线城市低,同时要接受薪资水平的下降。
西北、东北地区的相关岗位很少,确实想回到西北、东北家乡就业的同学可以考虑读研换专业方向或者考公。为了未来可以得到更好的发展、提高生活质量,建议到相对较近的发达城市就业。
3.5 箱线图
3.5.1 薪资与公司属性
import xlrd2
import pymysql
import matplotlib.pyplot as plt
from pyecharts.charts import Map
from pyecharts import options as opts
from pyecharts.faker import Faker
from pyecharts.charts import Geo
from pyecharts.globals import ChartType, SymbolType
import numpy as np
import pandas as pd
from pyecharts.charts import Sankey
import jieba
from imageio import imread
from wordcloud import WordCloud
db = pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="python上机",
charset="utf8"
)
def salary_xueli_boxplot(): # 主函数调用的salary_xueli_boxplot()函数
cursor = db.cursor()
cursor.execute("SELECT `薪资`,`学历要求` FROM after_clean")
all = cursor.fetchall()
# 创建两个空列表,用来存储筛选后的学历和薪资
xueli = []
salary = []
for each in all:
if each[1] != '' and each[1].split(',')[1] != '': # 筛除学历的缺失值以及部分异常值
xueli.append(each[1].split(',')[1])
# 筛除薪资的缺失值
salary.append(each[0].split('千/月')[0])
# print(each[1].split(',')[1],"+",each[0].split('千/月')[0])
# 对学历去重复值,并转换为列表形式,得到学历类型列表 xueli_after_quchong
xueli_after_quchong = list(set(xueli))
# 创建一个包含学历类型数个空列表的列表
final = []
for each in range(len(xueli_after_quchong)):
final.append([])
# 将不同类型学历的所有薪资先由字符串类型转换为浮点数类型,通过 index 索引,放进final列表中对应的空列表,得到每种学历对应的薪资列表final
for each in xueli_after_quchong:
for i in range(len(xueli)):
if xueli[i] == each:
final[xueli_after_quchong.index(each)].append(float(salary[i]))
# print(final)
# 画图
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.boxplot(final, labels=xueli_after_quchong)
plt.title('学历-薪资水平箱线图', fontsize=15)
plt.ylabel('薪资(单位:千/月)', fontsize=12)
plt.show() # 展示图形
cursor.close() # 关闭操作游标
if __name__ == '__main__':
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
rows = sheet.nrows # 获取行数
cols = sheet.ncols # 获取列数,尽管没用到
all_content = []
cols = sheet.col_values(3)
cols_s=sheet.col_values(6)
salary_xueli_boxplot(cols,cols_s)
由上图可知:
对于不同性质的企业,信管专业的平均薪资水平较为稳定,基本都在12000上下波动。但较高薪资普遍集中在民企、国企、外资企业,特别是民企。高薪晋中在民企(国企有断层现象,相关数据在收集时可能有个别岗位较为特殊),再次反映了我国互联网企业欣欣向荣的发展态势。
3.5.2 薪资与学历
代码同上一小节,箱线图如下。
由上图可知:
不同学历的薪资是有一定差异的,比如最低学历——初中及以下的平均薪资水平最低,硕博的平均薪资水平较高。
本科生的平均薪资高于大专生,而且本科生的高薪资(3万以上)高于大专生的高薪资水平(2-3万)。
由于爬取数据中学历要求为博士的岗位数据相对较少,所以绘制出的有关博士的箱线图与实际情况不相符合,后续研究中将对该类数据进行补充。
3.6 Sankey图
3.6.1 公司属性-学历要求桑基图
对公司属性、学历要求进行分类汇总(使用Excel的数据透视图功能),在此基础上进行桑基图的绘制,代码如下高亮部分所示,桑基图如图所示。
import pandas as pd
from generate_sankey import SanKey
import pandas as pd
df=pd.DataFrame({
'公司属性':['本科','本科','本科','本科','本科','本科','本科','博士','博士','博士','博士','博士','博士','博士','初中及以下','初中及以下','初中及以下','初中及以下','初中及以下','初中及以下','初中及以下','硕士','硕士','硕士','硕士','硕士','硕士','硕士','大专','大专','大专','大专','大专','大专','大专','中技/中专','中技/中专','中技/中专','中技/中专','中技/中专','中技/中专','中技/中专','高中','高中','高中','高中','高中','高中','高中'],
'学历要求':['5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上','5千/月以下','5-10千/月','10-15千/月','15-20千/月','20-25千/月','25-30千/月','30千/月以上'],
'数量':[4,1,0,0,0,0,132,0,0,0,1,3,1,2,0,15,1,1,0,0,0,0,19,56,55,42,39,33,114,2458,1502,534,257,66,56,17,145,31,3,2,0,0,16,106,22,2,2,0,0]
})
print(df)
nodes=[]
for i in range(2):
vales=df.iloc[:,i].unique()
for value in vales:
dic={}
dic['name']=value
nodes.append(dic)
print(nodes)
linkes=[]
for i in df.values:
dic={}
dic['source']=i[0]
dic['target']=i[1]
dic['value']=i[2]
linkes.append(dic)
print(linkes)
from pyecharts.charts import Sankey
from pyecharts import options as opts
pic=(
Sankey().add(
'',#图例名称
nodes,#传入节点数据
linkes,#传入边和流量数据
#设置透明度、弯曲度、颜色
linestyle_opt=opts.LineStyleOpts(opacity=0.3,curve=0.5,color='source'),
#标签显示位置
label_opts=opts.LabelOpts(position='right'),
#节点之间的距离
node_gap=30,
)
.set_global_opts(title_opts=opts.TitleOpts(title='学历-薪资桑基图'))
)
pic.render('/Users/xxx/test2.html')
由上图可知,本科生、大专生大多去了民营企业工作,硕士、博士等高学历人才也大多进入民企。由此我们可以推知目前我国互联网民企的发展较好,待遇薪资等符合当下年轻人的预期期望,较外企和国企等更有吸引力。
我们还可以从上图观察到:国企更偏向于招聘本科生,对高学历的人才需求不大。如果渴望进入国企工作,则可以考虑本科毕业就去工作,从而减轻后续考研的压力。
3.6.2 学历-薪资桑基图
该部分仍然采用数据透视表进行学历和薪资的统计,再使用python的第三方库绘制桑基图,代码与3.6.1节相似,这里不再赘述,桑基图如下所示。
由上图可知:
由于大专生的数量优势,所以在桑基图上可以看到大专学历的薪资分布较广泛,但其薪资普遍集中在5-15千/月,高薪资的人数占比较少。本科生的高薪比例较大,这可能与一些企业更注重技术有关,而不少本科生的专业实践能力较强,研究生、博士更注重科研,所以对本科生的需求大、薪资相对较高。
上图仅根据所爬取的数据绘制,并不能代表全局。学历要求为研究生、博士的岗位由于数量较少,所以在桑基图上不能表现出其薪资和学历优势,但结合实际,学历高对薪资是有较大影响的。
3.7 词云图
def wordcloud_welfare(a): #主函数调用的 wordcloud_welfare()函数
txt = ''
results=a
for each_result in results:
txt = txt + each_result
# 统计词频的字典
word_freq = dict()
# 装载停用词,此处需将资料中给出的 hit_stopwords.txt 文件放到本代码所在路径下
with open("/Users/xxx/stopwords.txt", "r", encoding='utf-8') as f1: # 读取我们的待处理本文
txt1 = f1.readlines()
stoplist = []
for line in txt1:
stoplist.append(line.strip('\n'))
# 切分、停用词过滤、统计词频
for w in list(jieba.cut(txt)):
if len(w) > 1 and w not in stoplist:
if w not in word_freq:
word_freq[w] = 1
else:
word_freq[w] = word_freq[w] + 1
# 指定背景模式图片
back_color = imread("/Users/xxx/school.png")
# 构造 WordCloud 对象
wc= WordCloud(background_color='white',max_words=100,collocations=False,width=1000,height=1000,font_path='simhei.ttf',mask=back_color)
# 调用方法生成词云图
wc = wc.generate_from_frequencies(word_freq)
# 保存图片
wc.to_file('WordCloud.png')
plt.imshow(wc)
plt.axis("off")
plt.show()
#创建主函数
if __name__ == '__main__':
worksheet = xlrd2.open_workbook('/Users/xxx/薪资匹配后表格.xls')
sheet_names = worksheet.sheet_names()
print(sheet_names)
for sheet_name in sheet_names:
sheet = worksheet.sheet_by_name(sheet_name)
rows = sheet.nrows # 获取行数
cols = sheet.ncols # 获取列数,尽管没用到
all_content = []
cols = sheet.col_values(2)
wordcloud_welfare(cols)
从词云图中我们可以看出所爬取岗位提供的主要待遇以及目前大家较为重视的福利项目。
人们“奖金”“绩效”的重视程度超过了对“五险一金”的重视,但这只是对所爬取的岗位的工作待遇的分析,并不能代表全局观点。同时,人们也渴望“弹性”的工作时间和工作地点,对工作环境、餐饮、交通便利程度的要求也在增强。
随着互联网行业对信管专业要求的提高,更多的就业者开始重视工作岗位能不能提供给他们持续学习、不断进步的机会,所以在词云图上我们可以看到“专业培训”的字体较大。
受新冠疫情影响和年轻一代思想观念的影响,当代就业者对健康生活、愉悦工作越来越向往,“医疗保险”、“体检”逐渐成为人们就业时关注的热点。工作环境的“氛围”感也开始成为一部分人对工作的期望点。
综上,我认为信管专业毕业生未来找工作时应结合以往大家关注的热点——“五险一金”,以及当下大家较为关注的“绩效奖金”、“年终奖金”、“专业培训”等待遇,同时根据自己更加关注和重视的待遇进行选择,找到适合自己的工作。