數(shù)據(jù)分析師薪酬?拉勾網(wǎng)職位爬取+分析

我是封面

——2018.06.01——
最近幾天看到了很多文章寫拉勾網(wǎng)職位爬取,那些基本是基于requests + json,但是貌似所給url打不開,還有可能被封ip的風(fēng)險。
本文主要用selenium + BautifulSoup + xpath工具,爬取不同城市的數(shù)據(jù)分析師薪酬,閑話不多說,上菜。

  • 爬蟲 selenium + BautifulSoup + xpath
  • 儲存數(shù)據(jù) MySQL
  • 數(shù)據(jù)清理
  • 薪酬和職位需求分析 seaborn + pyecharts
    ——
    編譯環(huán)境:Python 3.5
    IDE:jupyter notebook

1.網(wǎng)頁爬取

第一步,導(dǎo)入所需的模塊:

import re
import time
import random
#爬蟲工具
from selenium import webdriver 
from selenium.webdriver.chrome.options import Options
#解析工具
from bs4 import BeautifulSoup
from lxml import etree

第二步,設(shè)置瀏覽器參數(shù):

#設(shè)置無頭的chrome瀏覽器參數(shù)
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
#運(yùn)行瀏覽器
driver = webdriver.Chrome(chrome_options=chrome_options)
#備注::selenium 3.0+版本已不支持PhantomJS,所以這里使用無頭的chrome brower

第三步,找到合適url開始爬蟲,這里以長沙為例:

url = "https://www.lagou.com/jobs/list_數(shù)據(jù)分析師?px=default&city={}#filterBox".format('長沙')
driver.get(url)
time.sleep(3) #設(shè)置等待時間,強(qiáng)烈建議

第四步,解析網(wǎng)頁

# BeautifulSoup解析獲取職位的福利和標(biāo)簽
s = BeautifulSoup(driver.page_source,'lxml')
labels=[]
treatment=[]
for i in s.findAll('div','list_item_bot'):
    labels.append(i.text.split('“')[0].strip().replace('\n',','))
    treatment.append(i.text.split('“')[1].strip().replace('”',''))
    
# xpath 工具獲取公司名稱、鏈接、id等其他數(shù)據(jù)
selector=etree.HTML(driver.page_source)
company_link=selector.xpath('//div[@class="company_name"]/a/@href')
company_name=selector.xpath('//div[@class="company_name"]/a/text()')
job_name =selector.xpath('//a[@class="position_link"]/h3/text()')
job_link = selector.xpath('//a[@class="position_link"]/@href')
address =selector.xpath('//span[@class="add"]/em/text()')
temp_1 =selector.xpath('//div[@class="p_bot"]/div[@class="li_b_l"]/text()')
salary = selector.xpath('//span[@class="money"]/text()')
temp_2 = selector.xpath('//div[@class="industry"]/text()')
treatment = selector.xpath('//div[@class="li_b_r"]/text()')
    
#利用自定義的分隔函數(shù),得到所需的值
def spt(temp):
    list1,list2 = [],[]
    for i in temp:
        if i.strip():
            list1.append(i.split('/')[0].strip())
            list2.append(i.split('/')[1].strip())
    return list1,list2
experience,education = spt(temp_1)
industry,scale = spt(temp_2)
    
#從鏈接中獲取職位ID和所屬公司的ID
companyID = list(map(lambda x:re.findall(r'\d+',x)[0],company_link))
jobID = list(map(lambda x:re.findall(r'\d+',x)[0],job_link))

2.將數(shù)據(jù)存入MySQL數(shù)據(jù)庫中

在Python鏈接之前,已在MySQL數(shù)據(jù)庫,創(chuàng)建數(shù)據(jù)庫lagou,數(shù)據(jù)表job,company

#導(dǎo)入模塊 pymysql
import pymysql  

db = pymysql.connect("localhost", "root", "123456", "lagou",use_unicode=True, charset="utf8")
cursor = db.cursor()

#定義一個插入到數(shù)據(jù)庫的函數(shù)
def insert(x_zip,table):    
    for m in x_zip:
        sql = '''INSERT INTO {} values {}'''.format(table,m)
        try:
            cursor = db.cursor()
            cursor.execute(sql)
            db.commit()
        except Exception as error:
            db.rollback()
            print(error)
#將數(shù)據(jù)插入到數(shù)據(jù)庫中
companys = zip(companyID,company_name,company_link,industry,scale)
insert(companys,"company")
jobs = zip(jobID,job_name,address,experience,salary,treatment,job_link,companyID,education,labels,['長沙']*len(jobID))
insert(jobs,"job")

——
翻頁問題:
拉勾上的職位內(nèi)容基于js動態(tài)加載,所以點擊下一頁后,地址仍不生變化。但是仔細(xì)觀察elements,最后一頁會存在標(biāo)簽:<span …… class = 'pager_next pager_next_disabled' ,我們可以利用這個標(biāo)簽來定位。

if s.findAll('span','pager_next pager_next_disabled'):
    break
else:
    submitBtn = driver.find_element_by_class_name("pager_next")
    driver.execute_script("arguments[0].scrollIntoView()", submitBtn) # js滾動加載,缺少此步,action無法生效
    submitBtn.click()
    time.sleep(random.randint(3,20)) #爬蟲禮儀,減少訪問服務(wù)器的頻率

以上就是拉勾數(shù)據(jù)分析師職位數(shù)據(jù)爬取和儲存,這里為了顯得整潔,分別存儲在job,company兩個表中。為了便于閱讀,完整代碼放在文末鏈接中,有需要可以自己去fork。

3.數(shù)據(jù)清理

首先,導(dǎo)入數(shù)據(jù):

import pandas as pd
from sqlalchemy import create_engine
import pymysql

#創(chuàng)建engine
engine = create_engine('mysql://root:123456@localhost:3306/lagou?charset=utf8') 

#從數(shù)據(jù)庫讀取數(shù)據(jù)
#由于python 3 不支持MySQLdb,所以這里將文件中的MySQLdb,改為pymysql便可運(yùn)行
job = pd.read_sql('job',engine)
company = pd.read_sql('company',engine)

第二步,數(shù)據(jù)基本情況查看

job.head()  #前幾條數(shù)據(jù)查看
image
job.shape   #查看形狀
image
sum(job.duplicated())   #查看重復(fù)情況
image
job.info() #查看數(shù)據(jù)類型,空值存在情況
image

從以上的基本情況上,可以看到以下問題:

  1. 職位名稱中含有實習(xí)等因素,為了方便統(tǒng)計,這里去除實習(xí)職位的影響。
  2. experience、salary都是一個區(qū)間,后續(xù)探討experience、salary 和education 的關(guān)系需要將具體數(shù)值提取出來,轉(zhuǎn)換為float型數(shù)據(jù)
  3. 含有重復(fù)職位數(shù)據(jù),這里需要剔除。

第三步,清洗數(shù)據(jù)

#完全復(fù)制原dataframe中不重復(fù)的數(shù)據(jù),方便后續(xù)的數(shù)據(jù)修改
job_df = job.drop_duplicates().copy()
#去除實習(xí)數(shù)據(jù)
job_df.drop(job_df[job_df.name.str.contains('實習(xí)')].index,inplace=True)
#新建工資水平high\low
job_df['lowS'] = job_df['salary'].apply(lambda x: x.lower().split('-')[0].replace('k','')).astype(int)
job_df['highS'] = job_df['salary'].apply(lambda x: x.lower().split('-')[1].replace('k','')).astype(int)
#為了更符合實際,取工資的區(qū)間前25%
job_df['avgS'] = job_df['lowS']+(job_df['highS']-job_df['lowS'])/4 
#新建一個工作年限,處理經(jīng)驗問題
#若為不限/應(yīng)屆畢業(yè)生,值為0;
#若為1年以下或10年以上,值分別為1,10;
#若為1到3年之類的,值取二者均值
job_df['workyear'] = job_df['experience'].str.findall(r'\d+')
def avg_year(i):
    m = 0
    if len(i) == 0:
        m = 0
    elif len(i) == 1:
        m = int(i[0])
    else:
        m = sum(list(map(int,i)))/2
    return m
job_df['workyear'] = job_df['workyear'].apply(avg_year)

4.薪酬和職位分析

from pyecharts import Geo
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

matplotlib.rc('font', **{'family' : 'SimHei'}) #解決中文亂碼問題,必選
plt.style.use("ggplot")
#這里若不加plt.style.use("ggplot"),中文仍然亂碼
  • 薪酬整體分布狀況
plt.figure(figsize=(10,5))
sns.distplot(job_df['avgS'],color="r",kde=False)
plt.title('整體薪酬分布狀況');
image

薪酬集中在12000附近,整體上呈偏右分布。

  • 不同城市之間的薪水差異?
plt.figure(figsize=(14,5))
sns.boxplot(x='city',y='avgS',data=job_df,color="r")
plt.title('不同城市之間的平均薪水差異');
image

放在中國地圖上:

data_x = job_df.groupby('city')['avgS'].mean().reset_index()
geot = Geo("全國數(shù)據(jù)分析師平均薪酬分布", "數(shù)據(jù)來源:拉勾", title_color="#fff",
          title_pos="center", width=1200,
          height=600, background_color='#404a59')
attr= data_x['city'].values.tolist()
value = data_x['avgS'].values.tolist()
geot.add("",attr,value,type="heatmap", is_visualmap=True, visual_range=[0, 300],visual_text_color='#fff')
geot.render()
image

在地域分布的薪酬方面,北京、杭州和深圳的中位數(shù)薪酬牢牢的占據(jù)第一梯隊高于15k,其次就是上海、成都、武漢等城市,中位數(shù)薪酬也在11k左右,最低的是天津,這有可能是于需求上人才都轉(zhuǎn)移到北京去了。

  • 學(xué)歷對薪酬的影響?
plt.figure(figsize=(14,5))
sns.boxplot(x='education',y='avgS',data=job_df,color="r")
plt.title('不同學(xué)歷之間的薪水差異');
image

學(xué)歷越高,獲得高薪的機(jī)會也就越高,這說明讀書發(fā)家還是有機(jī)會的。

  • 工作經(jīng)驗對薪酬的影響?
plt.figure(figsize=(14,5))
sns.boxplot(x="experience", y="avgS", data=job_df)
sns.stripplot(x='experience',y='avgS',data=job_df,jitter=True, color ='.3')
plt.title('不同工作經(jīng)驗之間的薪水差異');
image

擬合來看:

sns.regplot(x='workyear',y='avgS',data=job_df,x_jitter=.1,)
plt.title('工作年限與薪酬的擬合關(guān)系')
image

整體上來看,隨著工作經(jīng)驗的增加,薪酬也在不斷上升。低的工作經(jīng)驗有也高薪的機(jī)會,但可能需要更高的技能要求(如普通業(yè)務(wù)運(yùn)營數(shù)據(jù)分析師和大數(shù)據(jù)挖掘工程師),不斷提升自我的能力才是王道。

  • 數(shù)據(jù)分析師的工作福利和技能
#定義詞云制作函數(shù)
def wordclouds(s):
    text = ''
    for line in job_df[s]:
        text = text+' '+line.strip().replace(',',' ')
    color_mask = imread('qqq.png')
    wordcloud = WordCloud(
                width=1000,height=600,
                font_path = 'simhei.ttf',
                background_color = 'white',
                mask = color_mask,
                max_words = 1000,
                max_font_size = 100, 
                collocations=False
                ).generate(text)
    wordcloud.to_file('{}.png'.format(s))
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()

#調(diào)用定義的詞云制作函數(shù)
wordclouds('labels')
wordclouds('treatment')
image
image

在職位標(biāo)簽上,更多的是數(shù)據(jù)挖掘類的工作,當(dāng)然像金融、大數(shù)據(jù)、業(yè)務(wù)運(yùn)營、機(jī)器學(xué)習(xí)這幾塊也必不可少的。
五險一金作為基本的福利牢牢站在第一福利關(guān)鍵詞,有些公司給出了六險一金(補(bǔ)充商業(yè)險)的待遇也很不錯的,帶薪年假彈性工作、周末雙休牢牢吸引著眼球吧。
最后,我們探討一下,數(shù)據(jù)分析師的需求問題:

  • 數(shù)據(jù)分析師的主要分布行業(yè)?
#合并工作職位和企業(yè)
df = company_df[['id','name','industry','scale']].merge(job_df[['id','company_id','avgS']],left_on='id',right_on='company_id')

#將industy里面的關(guān)鍵詞取出來
industry_df = pd.DataFrame(df.industry.apply(lambda x: x.replace(' ,',' ').replace(',',' ').replace('、',' ').strip().split(' ')).tolist())
industry_df_new = industry_df.stack().reset_index().rename(columns={'level_0':'df_index',0:'industry_name'})
industry_df_new.drop('level_1', axis=1, inplace=True)

t = df.merge(industry_df_new,right_on='df_index',left_index=True)
tt['industry_name'].value_counts().plot.bar(figsize =(10,4),title=('數(shù)據(jù)分析師的需求行業(yè)分布'));
image

移動互聯(lián)網(wǎng)需求獨(dú)占鰲頭,其次就是金融、數(shù)據(jù)服務(wù)類、電子商務(wù)類公司需求較大,當(dāng)然可能會有部分企業(yè)行業(yè)標(biāo)簽重合。

  • 全國各地職位需求數(shù)分布問題?
#提取每個城市的職位數(shù)
data = job_df.groupby('city')['name'].count().reset_index()

#作圖scatter
geo = Geo("全國數(shù)據(jù)分析師的需求分布", "數(shù)據(jù)來源:拉勾", title_color="#fff",
          title_pos="center", width=1200,
          height=600, background_color='#404a59')
attr= data['city'].values.tolist()
value = data['name'].values.tolist()
geo.add("",attr,value,type="scatter", is_visualmap=True, visual_range=[50, 2000],visual_text_color='#fff',visual_type='size', visual_range_size=[20, 80])
geo.render()
image

求職機(jī)會上,雖然說一線城市機(jī)會多,但一些準(zhǔn)一線城市也在蓬勃發(fā)展中。

結(jié)論?

  1. 數(shù)據(jù)分析師的平均薪水集中在12k附近,整體上呈右分布
  2. 隨著經(jīng)驗和學(xué)歷的增加,整體上來看數(shù)據(jù)分析師的薪酬在不斷上升中。
  3. 目前市場上數(shù)據(jù)分析師的人才缺口,更多的是需要掌握數(shù)據(jù)挖掘的技能。
  4. 數(shù)據(jù)分析師的需求方面,移動互聯(lián)網(wǎng)需求獨(dú)占鰲頭,其次就是金融、數(shù)據(jù)服務(wù)類、電子商務(wù)類公司需求較大
  5. 數(shù)據(jù)分析師的求職方面,雖然說一線城市機(jī)會多,但一些準(zhǔn)一線城市也在蓬勃發(fā)展中。
    ?

局限性:

  1. 事實上,一線城市北上廣深杭的職位可能會遠(yuǎn)多于其他城市,因為拉勾僅限獲取前30頁,這些城市的職位或多或少大于30頁的。
  2. 數(shù)據(jù)分析師的技能是影響薪酬的重要因素,但本次分析并沒有體現(xiàn)出來。

——
爬蟲+分析完整代碼:https://github.com/mimicolois/lagou

PS:
1.git 是非常強(qiáng)大的工具,學(xué)會使用git,從注冊一個github賬號開始
2.嫌棄國內(nèi)訪問速度,代碼托管國內(nèi)有碼云
3.git 入門指南:廖雪峰老師的Git教程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容