前言:
8个小时内完成爬虫,数据清洗并可视化。因为自己也是小白,做的时候时间还挺赶的。很多地方没有做到完美,比如一些数据清洗的步骤走了捷径。有不足的地方,欢迎大神们留言指教。
selenium :3.141.0
pyecharts:1.9.0
我个人是喜欢用selenium做爬虫的,可以享受web自动化的这个过程。因为要爬取的东西不是很多,如果要爬取很多内容的同学,还是老老实实用request的吧。
此次爬取的是51job前程无忧,这里的url可以替换成你想查询的该网站的任何职业或者岗位。
爬取几个我们需要的重要信息,包括:职位名称,发布日期,工资,学历要求,经验要求,工作地点等等等。
网速快的同学记得把睡眠时间调短一些,不然真的会爬很久。我自己大概爬了7.8分钟吧
from selenium import webdriver
import pandas as pd
import time
wd = webdriver.Chrome()
wd.get('https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25BB%25BA%25E7%25AD%2591%25E8%25AE%25BE%25E8%25AE%25A1,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=')
wd.implicitly_wait(10)
max_pages = 100
titles = []
times = []
wages = []
infos = []
company_names = []
company_infos = []
company_atrri = []
def get_data(titles,times,wages,infos,company_names,company_infos,company_atrri):
#获取职位名称
title0 = wd.find_elements_by_css_selector('.jname.at')
for each_title in title0:
titles.append(each_title.text)
#获取发布时间
time0 = wd.find_elements_by_css_selector('.time')
for each_time in time0:
times.append(each_time.text)
#获取工资信息
wage0 = wd.find_elements_by_css_selector('.sal')
for each_wage in wage0:
wages.append(each_wage.text)
#获取更多职位信息
info0 = wd.find_elements_by_css_selector('.d.at')
for each_info in info0:
infos.append(each_info.text)
#获取公司信息
company_name0 = wd.find_elements_by_css_selector('.cname.at')
for each_c_name in company_name0:
company_names.append(each_c_name.text)
#获取更多公司信息
company_info0 = wd.find_elements_by_css_selector('.dc.at')
for each_c_info in company_info0:
company_infos.append(each_c_info.text)
#获取公司性质
company_atrri0 = wd.find_elements_by_css_selector('.int.at')
for each_c_attr in company_atrri0:
company_atrri.append(each_c_attr.text)
for i in range(max_pages):
print(f'正在爬取第{i}页')
get_data(titles,times,wages,infos,company_names,company_infos,company_atrri)
wd.find_element_by_css_selector('.next').click()
time.sleep(5) # 网速快的同学,请在这里操作,过快的操作可能会被检验出IP异常
data = {
'岗位名称': titles,
'发布时间':times,
'薪酬':wages,
'更多职位信息':infos,
'公司名称':company_names,
'公司更多信息':company_infos,
'公司性质':company_atrri
}
df = pd.DataFrame(data)
df.to_csv('建筑岗位信息.csv')
wd.quit()
最后将爬取的5000条信息转存为CSV格式。
下面是部分结果展示:
这里清洗数据的工作是比较乱的,因为对我来说时间太紧了。下次会发一个另外的爬虫悉尼房价的代码,想对来说清洗很多。
清洗数据的几个主要目的:
import pandas as pd
pd.set_option('display.max_columns', None)
df = pd.read_csv('建筑岗位信息.csv')
df = df.iloc[:2500,:] # 只取建筑设计师,其他相关岗位全部删除
#print(df['薪酬'])
#统一一下单位,因为时间原因,把年薪,和以千为单位的数据全部删除掉了,如果认真做,千万不要删除,每一条数据都很重要
wage_types = []
wage_types2 = []
year = ['年']
month = ['月']
wagek = ['千']
wagew = ['万']
df.loc[:, "薪酬"] = df["薪酬"].astype('str')
for i in range(len(df['薪酬'])):
if any(key in df['薪酬'][i] for key in year):
wage_types.append('年薪')
elif any(key in df['薪酬'][i] for key in month):
wage_types.append('月薪')
else:
wage_types.append("None")
df['薪酬类型1'] = wage_types
df = df[df['薪酬类型1'] != 'None']
df = df[df['薪酬类型1'] != '年薪']
df.drop(columns = ['Unnamed: 0'],inplace = True)
df = df.reset_index(drop=True)
df.loc[:,'薪酬'] = df['薪酬'].str.replace("/月",'').astype('str')
for i in range(len(df['薪酬'])):
if any(key in df['薪酬'][i] for key in wagek):
wage_types2.append('千')
elif any(key in df['薪酬'][i] for key in wagew):
wage_types2.append('万')
df['薪酬类型2'] = wage_types2
#把数字后面的单位(千万)提出来
for i in range(len(df['薪酬'])):
if df['薪酬类型2'][i] == '千':
df['薪酬'][i] = df['薪酬'][i].replace('千', '')
elif df['薪酬类型2'][i] == '万':
df['薪酬'][i] = df['薪酬'][i].replace('万', '')
#找出工资的上下线
min_wage = []
max_wage = []
for i in range(len(df['薪酬'])):
try:
min_wage0= df['薪酬'][i].split("-")[0]
max_wage0 = df['薪酬'][i].split("-")[1]
min_wage.append(min_wage0)
max_wage.append(max_wage0)
except:
min_wage0 = 0
max_wage0 = 0
min_wage.append(min_wage0)
max_wage.append(max_wage0)
df['minwage'] = min_wage
df['maxwage'] = max_wage
df['minwage'] = df["minwage"].astype('float')
df['maxwage'] =df["maxwage"].astype('float')
areas = []
exps = []
degrees = []
for i in range(len(df['更多职位信息'])):
area = df['更多职位信息'][i].split('|')[0]
areas.append(area)
exp = df['更多职位信息'][i].split('|')[1]
exps.append(exp)
try:
degree = df['更多职位信息'][i].split('|')[2]
degrees.append(degree)
except:
degree = "None"
degrees.append(degree)
df['上班地点'] = areas
df['经验要求'] = exps
df['学历要求'] = degrees
df = df[df['薪酬类型2'] != '千']
df.drop(['薪酬','更多职位信息','薪酬类型1'],axis = 1,inplace = True)
df['薪酬'] = (df['minwage']+df['maxwage'])/2
df.drop(['minwage','maxwage','薪酬类型2'],axis = 1,inplace = True)
df= df[df['薪酬'] != 0]
df = df.reset_index(drop=True)
df.to_csv('清洗过后的数据.csv')
pyecharts真的是神仙工具,除了网上的教程不那么详细之外,几乎找不到啥子缺点(好像这是我自己的缺点。。。)。pyecharts输出的是html文件,可以用浏览器打开,并且都是交互式体验,个人觉得和tableau做出来的图差距不大了。还有各种像水滴图,旭日图各种各样的图都ok!度娘良心出品。当然也没有忘记我们的老朋友matlibplot,下面也有用它做做图。具体选用什么可视库大家随喜好就好。
在可视化的基础上我写了一些自己的结论不一定对。(毕竟这行业我也不是太了解)
import pandas as pd
from pyecharts.charts import Pie,Bar
from pyecharts import options as opts
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
#这部分数据groupby手滑被我删了,自己groupby一下吧。实在不会,代码和下面的上班地点是一样的
cate = ['房地产', '建筑', '多元化业务集团公司', '家居/室内设计','专业服务','石油/化工']
values = [935,624,73,25,24,21]
pie = (Pie()
.add('',[list(z) for z in zip(cate,values)],
radius=['40%','60%'],
label_opts=opts.LabelOpts(is_show=True),
)
.set_global_opts(title_opts=opts.TitleOpts(title = '建筑行业岗位分布'),
legend_opts=opts.LegendOpts(is_show=True))
)
pie.render("社招-建筑行业岗位分布.html")
df = pd.read_csv("清洗过后的数据.csv")
#print(df.groupby('公司性质')['薪酬'].mean())
"""
岗位更多的集中在房地产(甲方)和建筑事务所/设计院(乙方)
传统建筑行业的平均薪酬在1.303w每个月,房地产行业的平均薪酬在1.85W每个月,平均薪酬最高的是采矿页2.75w每个月。
但是这个统计不完全正确,单纯的比较不具有意义,因为同时应该考虑到房地产行业是否对经验或者学历要求更高。
"""
#上班地点可视化
area_group = df.groupby('上班地点').count()
#print(area_group.sort_values(by = '薪酬',ascending= False))
cate = ["异地招聘",'深圳南山', '深圳福田', '广州天河', '成都高新','上海','上海浦东','广州','成都','南京']
values = [174,41,37,32,32,28,26,22,21,18]
pie = (Pie()
.add('',[list(z) for z in zip(cate,values)],
radius=['40%','60%'],
label_opts=opts.LabelOpts(is_show=True),
)
.set_global_opts(title_opts=opts.TitleOpts(title = '建筑行业地区分布'),
legend_opts=opts.LegendOpts(is_show=True))
)
pie.render("社招-建筑行业地区分布.html")
#print(df.groupby('上班地点')['薪酬'].mean())
"""
建筑设计师的需求想对来说比较分散,全国各地都有需要。不像一些特殊岗位如金融,计算机的需求会比较聚集在超一线城市。
在不考虑经验的情况下。可以看超一线城市的平均薪酬是高于一般一线城市的,大约在1.8+W的位置。
不过东莞,四川,重庆等特殊地区对建筑设的需求明显大于其他同类城市的。
"""
#学历要求
degree_group = df.groupby('学历要求').count()
#print(degree_group.sort_values(by = '薪酬',ascending= False))
cate = ["本科",'大专', '未知', '硕士', '中专']
values = [1343,267,39,10,1]
bar = (Bar()
.add_xaxis(cate)
.add_yaxis('', values)
.set_series_opts(label_opts=opts.LabelOpts(is_show=True, font_size=15))
.set_global_opts(title_opts=opts.TitleOpts(title='社招-建筑行业学历要求'),
xaxis_opts=opts.AxisOpts(name='学历要求'),
yaxis_opts=opts.AxisOpts(name='样本数量'))
)
bar.render('社招-建筑行业学历要求.html')
print(df.groupby('学历要求')['薪酬'].mean())
"""
根据爬取到的1600多条建筑师招聘信息来看,学历和薪酬之间不存在明显的线性关系。(在更高维度或者更多样本的时候,可能可以够找到一些关系)
通常来说我们认为建筑设计师对于学历并没有太硬性的要求,更多的还是个人技能的要求。
"""
#经验要求
exp_group = df.groupby('经验要求').count()
#print(exp_group.sort_values(by = '薪酬',ascending= False))
cate = ["5-7年",'3-4年', '2年经验', '8-9年', '1年经验','10年以上经验','无需经验']
values = [666,532,151,99,91,56,39]
bar = (Bar()
.add_xaxis(cate)
.add_yaxis('', values)
.set_series_opts(label_opts=opts.LabelOpts(is_show=True, font_size=15))
.set_global_opts(title_opts=opts.TitleOpts(title='社招-建筑行业经验要求'),
xaxis_opts=opts.AxisOpts(name='经验要求'),
yaxis_opts=opts.AxisOpts(name='样本数量'))
)
bar.render('社招-建筑行业经历要求.html')
#print(df.groupby('经验要求')['薪酬'].mean())
"""
可以看出3到7年占了几乎3分之2的样本,说明这个时间段才是建筑设计师的黄金时间。
与学历不同,薪酬和工作经历之间存在一个明显的正相关关系,10年以上的平均工资在2.36w/月。
而一年工作经验的1.07W/月,2年1.17w/月.
"""
'''
x = ['1','2','3-4','5-7','8-9','10+']
y= [1.07,1.17,1.43,1.86,2.33,2.36]
plt.xlabel('working experiences')
plt.ylabel('wages')
plt.title("The relation between Wages and Working Experiences")
plt.plot(x,y)
plt.savefig("工作经验和工资关系.png")'''