
 隨著科技的飛速發(fā)展,數(shù)據(jù)呈現(xiàn)爆發(fā)式的增長,任何人都擺脫不了與數(shù)據(jù)打交道,社會對于“數(shù)據(jù)”方面的人才需求也在不斷增大。因此了解當(dāng)下企業(yè)究竟需要招聘什么樣的人才?需要什么樣的技能?不管是對于在校生,還是對于求職者來說,都顯得很有必要。本文基于這個問題,針對51job招聘網(wǎng)站,爬取了全國范圍內(nèi)大數(shù)據(jù)、數(shù)據(jù)分析、數(shù)據(jù)挖掘、機器學(xué)習(xí)、人工智能等相關(guān)崗位的招聘信息。分析比較了不同崗位的薪資、學(xué)歷要求;分析比較了不同區(qū)域、行業(yè)對相關(guān)人才的需求情況;分析比較了不同崗位的知識、技能要求等。信息的爬取(基于51job招聘網(wǎng)站的數(shù)據(jù)爬取)
爬取崗位:大數(shù)據(jù)、數(shù)據(jù)分析、機器學(xué)習(xí)、人工智能等相關(guān)崗位; 爬取字段:公司名、崗位名、工作地址、薪資、發(fā)布時間、工作描述、公司類型、員工人數(shù)、所屬行業(yè); 說明:基于51job招聘網(wǎng)站,我們搜索全國對于“數(shù)據(jù)”崗位的需求,大概有2000頁。我們爬取的字段,既有一級頁面的相關(guān)信息,還有二級頁面的部分信息; 爬取思路:先針對某一頁數(shù)據(jù)的一級頁面做一個解析,然后再進(jìn)行二級頁面做一個解析,最后再進(jìn)行翻頁操作; 使用工具:Python+requests+lxml+pandas+time 網(wǎng)站解析方式:Xpath
1)導(dǎo)入相關(guān)庫 import requests import pandas as pd from pprint import pprint from lxml import etree import time import warnings warnings.filterwarnings('ignore')
# 第一頁的特點 https://search./list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,1.html? # 第二頁的特點 https://search./list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,2.html? # 第三頁的特點 https://search./list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,3.html? 注意:通過對于頁面的觀察,可以看出,就一個地方的數(shù)字變化了,因此只需要做字符串拼接,然后循環(huán)爬取即可。import requests import pandas as pd from pprint import pprint from lxml import etree import time import warnings warnings.filterwarnings('ignore')
for i in range(1,1501): print('正在爬取第' + str(i) + '頁的數(shù)據(jù)') url_pre = 'https://search./list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,' url_end = '.html?' url = url_pre + str(i) + url_end headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } web = requests.get(url, headers=headers) web.encoding = 'gbk' dom = etree.HTML(web.text) # 1、崗位名稱 job_name = dom.xpath('//div[@class='dw_table']/div[@class='el']//p/span/a[@target='_blank']/@title') # 2、公司名稱 company_name = dom.xpath('//div[@class='dw_table']/div[@class='el']/span[@class='t2']/a[@target='_blank']/@title') # 3、工作地點 address = dom.xpath('//div[@class='dw_table']/div[@class='el']/span[@class='t3']/text()') # 4、工資 salary_mid = dom.xpath('//div[@class='dw_table']/div[@class='el']/span[@class='t4']') salary = [i.text for i in salary_mid] # 5、發(fā)布日期 release_time = dom.xpath('//div[@class='dw_table']/div[@class='el']/span[@class='t5']/text()') # 6、獲取二級網(wǎng)址url deep_url = dom.xpath('//div[@class='dw_table']/div[@class='el']//p/span/a[@target='_blank']/@href') RandomAll = [] JobDescribe = [] CompanyType = [] CompanySize = [] Industry = [] for i in range(len(deep_url)): web_test = requests.get(deep_url[i], headers=headers) web_test.encoding = 'gbk' dom_test = etree.HTML(web_test.text) # 7、爬取經(jīng)驗、學(xué)歷信息,先合在一個字段里面,以后再做數(shù)據(jù)清洗。命名為random_all random_all = dom_test.xpath('//div[@class='tHeader tHjob']//div[@class='cn']/p[@class='msg ltype']/text()') # 8、崗位描述性息 job_describe = dom_test.xpath('//div[@class='tBorderTop_box']//div[@class='bmsg job_msg inbox']/p/text()') # 9、公司類型 company_type = dom_test.xpath('//div[@class='tCompany_sidebar']//div[@class='com_tag']/p[1]/@title') # 10、公司規(guī)模(人數(shù)) company_size = dom_test.xpath('//div[@class='tCompany_sidebar']//div[@class='com_tag']/p[2]/@title') # 11、所屬行業(yè)(公司) industry = dom_test.xpath('//div[@class='tCompany_sidebar']//div[@class='com_tag']/p[3]/@title') # 將上述信息保存到各自的列表中 RandomAll.append(random_all) JobDescribe.append(job_describe) CompanyType.append(company_type) CompanySize.append(company_size) Industry.append(industry) # 為了反爬,設(shè)置睡眠時間 time.sleep(1) # 由于我們需要爬取很多頁,為了防止最后一次性保存所有數(shù)據(jù)出現(xiàn)的錯誤,因此,我們每獲取一夜的數(shù)據(jù),就進(jìn)行一次數(shù)據(jù)存取。 df = pd.DataFrame() df['崗位名稱'] = job_name df['公司名稱'] = company_name df['工作地點'] = address df['工資'] = salary df['發(fā)布日期'] = release_time df['經(jīng)驗、學(xué)歷'] = RandomAll df['公司類型'] = CompanyType df['公司規(guī)模'] = CompanySize df['所屬行業(yè)'] = Industry df['崗位描述'] = JobDescribe # 這里在寫出過程中,有可能會寫入失敗,為了解決這個問題,我們使用異常處理。 try: df.to_csv('job_info.csv', mode='a+', header=None, index=None, encoding='gbk') except: print('當(dāng)頁數(shù)據(jù)寫入失敗') time.sleep(1) print('數(shù)據(jù)爬取完畢,是不是很開心?。?!') 這里可以看到,我們爬取了1000多頁的數(shù)據(jù)做最終的分析。因此每爬取一頁的數(shù)據(jù),做一次數(shù)據(jù)存儲,避免最終一次性存儲導(dǎo)致失敗。同時根據(jù)自己的測試,有一些頁數(shù)進(jìn)行數(shù)據(jù)存儲,會導(dǎo)致失敗,為了不影響后面代碼的執(zhí)行,我們使用了“try-except”異常處理。在一級頁面中,我們爬取了“崗位名稱”,“公司名稱”,“工作地點”,“工資”,“發(fā)布日期”,“二級網(wǎng)址的url”這幾個字段。在二級頁面中,我們爬取了“經(jīng)驗、學(xué)歷信息”,“崗位描述”,“公司類型”,“公司規(guī)?!?,“所屬行業(yè)”這幾個字段。從爬取到的數(shù)據(jù)中截取部分做了一個展示,可以看出數(shù)據(jù)很亂。雜亂的數(shù)據(jù)并不利于我們的分析,因此需要根據(jù)研究的目標(biāo)做一個數(shù)據(jù)預(yù)處理,得到我們最終可以用來做可視化展示的數(shù)據(jù)。1)相關(guān)庫的導(dǎo)入及數(shù)據(jù)的讀取df = pd.read_csv(r'G:\8泰迪\python_project\51_job\job_info1.csv',engine='python',header=None) # 為數(shù)據(jù)框指定行索引 df.index = range(len(df)) # 為數(shù)據(jù)框指定列索引 df.columns = ['崗位名','公司名','工作地點','工資','發(fā)布日期','經(jīng)驗與學(xué)歷','公司類型','公司規(guī)模','行業(yè)','工作描述'] 我們認(rèn)為一個公司的公司名和和發(fā)布的崗位名一致,就看作是重復(fù)值。因此,使用drop_duplicates(subset=[])函數(shù),基于“崗位名”和“公司名”做一個重復(fù)值的剔除。# 去重之前的記錄數(shù) print('去重之前的記錄數(shù)',df.shape) # 記錄去重 df.drop_duplicates(subset=['公司名','崗位名'],inplace=True) # 去重之后的記錄數(shù) print('去重之后的記錄數(shù)',df.shape)
df['崗位名'].value_counts() df['崗位名'] = df['崗位名'].apply(lambda x:x.lower()) 說明:首先我們對每個崗位出現(xiàn)的頻次做一個統(tǒng)計,可以看出“崗位名字段”太雜亂,不便于我們做統(tǒng)計分析。接著我們將崗位名中的大寫英文字母統(tǒng)一轉(zhuǎn)換為小寫字母,也就是說“AI”和“Ai”屬于同一個東西。② 構(gòu)造想要分析的目標(biāo)崗位,做一個數(shù)據(jù)篩選job_info.shape target_job = ['算法', '開發(fā)', '分析', '工程師', '數(shù)據(jù)', '運營', '運維'] index = [df['崗位名'].str.count(i) for i in target_job] index = np.array(index).sum(axis=0) > 0 job_info = df[index] job_info.shape 說明:首先我們構(gòu)造了如上七個目標(biāo)崗位的關(guān)鍵字眼。然后利用count()函數(shù)統(tǒng)計每一條記錄中,是否包含這七個關(guān)鍵字眼,如果包含就保留這個字段,不過不包含就刪除這個字段。最后查看篩選之后還剩余多少條記錄。③ 目標(biāo)崗位標(biāo)準(zhǔn)化處理(由于目標(biāo)崗位太雜亂,我們需要統(tǒng)一一下)job_list = ['數(shù)據(jù)分析', '數(shù)據(jù)統(tǒng)計','數(shù)據(jù)專員','數(shù)據(jù)挖掘', '算法', '大數(shù)據(jù)','開發(fā)工程師', '運營', '軟件工程', '前端開發(fā)', '深度學(xué)習(xí)', 'ai', '數(shù)據(jù)庫', '數(shù)據(jù)庫', '數(shù)據(jù)產(chǎn)品', '客服', 'java', '.net', 'andrio', '人工智能', 'c++', '數(shù)據(jù)管理','測試','運維'] job_list = np.array(job_list) def rename(x=None,job_list=job_list): index = [i in x for i in job_list] if sum(index) > 0: return job_list[index][0] else: return x job_info['崗位名'] = job_info['崗位名'].apply(rename) job_info['崗位名'].value_counts() # 數(shù)據(jù)統(tǒng)計、數(shù)據(jù)專員、數(shù)據(jù)分析統(tǒng)一歸為數(shù)據(jù)分析 job_info['崗位名'] = job_info['崗位名'].apply(lambda x:re.sub('數(shù)據(jù)專員','數(shù)據(jù)分析',x)) job_info['崗位名'] = job_info['崗位名'].apply(lambda x:re.sub('數(shù)據(jù)統(tǒng)計','數(shù)據(jù)分析',x)) 說明:首先我們定義了一個想要替換的目標(biāo)崗位job_list,將其轉(zhuǎn)換為ndarray數(shù)組。然后定義一個函數(shù),如果某條記錄包含job_list數(shù)組中的某個關(guān)鍵詞,那么就將該條記錄替換為這個關(guān)鍵詞,如果某條記錄包含job_list數(shù)組中的多個關(guān)鍵詞,我們只取第一個關(guān)鍵詞替換該條記錄。接著使用value_counts()函數(shù)統(tǒng)計一下替換后的各崗位的頻次。最后,我們將“數(shù)據(jù)專員”、“數(shù)據(jù)統(tǒng)計”統(tǒng)一歸為“數(shù)據(jù)分析”。工資水平字段的數(shù)據(jù)類似于“20-30萬/年”、“2.5-3萬/月”和“3.5-4.5千/月”這樣的格式。我們需要做一個統(tǒng)一的變化,將數(shù)據(jù)格式轉(zhuǎn)換為“元/月”,然后取出這兩個數(shù)字,求一個平均值。job_info['工資'].str[-1].value_counts() job_info['工資'].str[-3].value_counts()
index1 = job_info['工資'].str[-1].isin(['年','月']) index2 = job_info['工資'].str[-3].isin(['萬','千']) job_info = job_info[index1 & index2]
def get_money_max_min(x): try: if x[-3] == '萬': z = [float(i)*10000 for i in re.findall('[0-9]+\.?[0-9]*',x)] elif x[-3] == '千': z = [float(i) * 1000 for i in re.findall('[0-9]+\.?[0-9]*', x)] if x[-1] == '年': z = [i/12 for i in z] return z except: return x
salary = job_info['工資'].apply(get_money_max_min) job_info['最低工資'] = salary.str[0] job_info['最高工資'] = salary.str[1] job_info['工資水平'] = job_info[['最低工資','最高工資']].mean(axis=1) 說明:首先我們做了一個數(shù)據(jù)篩選,針對于每一條記錄,如果最后一個字在“年”和“月”中,同時第三個字在“萬”和“千”中,那么就保留這條記錄,否則就刪除。接著定義了一個函數(shù),將格式統(tǒng)一轉(zhuǎn)換為“元/月”。最后將最低工資和最高工資求平均值,得到最終的“工資水平”字段。由于整個數(shù)據(jù)是關(guān)于全國的數(shù)據(jù),涉及到的城市也是特別多。我們需要自定義一個常用的目標(biāo)工作地點字段,對數(shù)據(jù)做一個統(tǒng)一處理。#job_info['工作地點'].value_counts() address_list = ['北京', '上海', '廣州', '深圳', '杭州', '蘇州', '長沙', '武漢', '天津', '成都', '西安', '東莞', '合肥', '佛山', '寧波', '南京', '重慶', '長春', '鄭州', '常州', '福州', '沈陽', '濟(jì)南', '寧波', '廈門', '貴州', '珠海', '青島', '中山', '大連','昆山','惠州','哈爾濱','昆明','南昌','無錫'] address_list = np.array(address_list)
def rename(x=None,address_list=address_list): index = [i in x for i in address_list] if sum(index) > 0: return address_list[index][0] else: return x job_info['工作地點'] = job_info['工作地點'].apply(rename) 說明:首先我們定義了一個目標(biāo)工作地點列表,將其轉(zhuǎn)換為ndarray數(shù)組。接著定義了一個函數(shù),將原始工作地點記錄,替換為目標(biāo)工作地點中的城市。job_info.loc[job_info['公司類型'].apply(lambda x:len(x)<6),'公司類型'] = np.nan job_info['公司類型'] = job_info['公司類型'].str[2:-2] 每個公司的行業(yè)字段可能會有多個行業(yè)標(biāo)簽,但是我們默認(rèn)以第一個作為該公司的行業(yè)標(biāo)簽。# job_info['行業(yè)'].value_counts() job_info['行業(yè)'] = job_info['行業(yè)'].apply(lambda x:re.sub(',','/',x)) job_info.loc[job_info['行業(yè)'].apply(lambda x:len(x)<6),'行業(yè)'] = np.nan job_info['行業(yè)'] = job_info['行業(yè)'].str[2:-2].str.split('/').str[0] 關(guān)于這個字段的數(shù)據(jù)處理,我很是思考了一會兒,不太好敘述,放上代碼自己下去體會。job_info['學(xué)歷'] = job_info['經(jīng)驗與學(xué)歷'].apply(lambda x:re.findall('本科|大專|應(yīng)屆生|在校生|碩士',x)) def func(x): if len(x) == 0: return np.nan elif len(x) == 1 or len(x) == 2: return x[0] else: return x[2] job_info['學(xué)歷'] = job_info['學(xué)歷'].apply(func) 對于每一行記錄,我們?nèi)コS迷~以后,做一個jieba分詞。with open(r'G:\8泰迪\python_project\51_job\stopword.txt','r') as f: stopword = f.read() stopword = stopword.split() stopword = stopword + ['任職','職位',' ']
job_info['工作描述'] = job_info['工作描述'].str[2:-2].apply(lambda x:x.lower()).apply(lambda x:''.join(x))\ .apply(jieba.lcut).apply(lambda x:[i for i in x if i not in stopword]) job_info.loc[job_info['工作描述'].apply(lambda x:len(x) < 6),'工作描述'] = np.nan
#job_info['公司規(guī)模'].value_counts() def func(x): if x == '['少于50人']': return '<50' elif x == '['50-150人']': return '50-150' elif x == '['150-500人']': return '150-500' elif x == '['500-1000人']': return '500-1000' elif x == '['1000-5000人']': return '1000-5000' elif x == '['5000-10000人']': return '5000-10000' elif x == '['10000人以上']': return '>10000' else: return np.nan job_info['公司規(guī)模'] = job_info['公司規(guī)模'].apply(func) 我們針對最終清洗干凈的數(shù)據(jù),選取需要分析的字段,做一個數(shù)據(jù)存儲。feature = ['公司名','崗位名','工作地點','工資水平','發(fā)布日期','學(xué)歷','公司類型','公司規(guī)模','行業(yè)','工作描述'] final_df = job_info[feature] final_df.to_excel(r'G:\8泰迪\python_project\51_job\詞云圖.xlsx',encoding='gbk',index=None) 由于我們之后需要針對不同的崗位名做不同的詞云圖處理,并且是在tableau中做可視化展示,因此我們需要按照崗位名分類,求出不同崗位下各關(guān)鍵詞的詞頻統(tǒng)計。import numpy as np import pandas as pd import re import jieba import warnings warnings.filterwarnings('ignore')
df = pd.read_excel(r'G:\8泰迪\python_project\51_job\new_job_info1.xlsx',encoding='gbk') df
def get_word_cloud(data=None, job_name=None): words = [] describe = data['工作描述'][data['崗位名'] == job_name].str[1:-1] describe.dropna(inplace=True) [words.extend(i.split(',')) for i in describe] words = pd.Series(words) word_fre = words.value_counts() return word_fre
zz = ['數(shù)據(jù)分析', '算法', '大數(shù)據(jù)','開發(fā)工程師', '運營', '軟件工程','運維', '數(shù)據(jù)庫','java','測試'] for i in zz: word_fre = get_word_cloud(data=df, job_name='{}'.format(i)) word_fre = word_fre[1:].reset_index()[:100] word_fre['崗位名'] = pd.Series('{}'.format(i),index=range(len(word_fre))) word_fre.to_csv(r'G:\8泰迪\python_project\51_job\詞云圖\bb.csv', mode='a',index=False, header=None,encoding='gbk') 說明:這里最終就不做結(jié)論分析了,因為結(jié)論通過上圖,就可以很清晰的看出來。聲明:本文為CSDN博主「Huang supreme」的原創(chuàng)文章,聲明:本文為CSDN博主「Huang supreme」的原創(chuàng)文章,CSDN 官方經(jīng)授權(quán)發(fā)布。
|