用Python爬取招聘網站數據,讓學習、求職信息公開透明化~

1、項目背景

隨着科技的飛速發展,數據呈現爆發式的增長,任何人都擺脫不了與數據打交道,社會對於“數據”方面的人才需求也在不斷增大。因此瞭解當下企業究竟需要招聘什麼樣的人才?需要什麼樣的技能?不管是對於在校生,還是對於求職者來說,都顯得很有必要。

本文基於這個問題,針對51job招聘網站,爬取了全國範圍內大數據、數據分析、數據挖掘、機器學習、人工智能等相關崗位的招聘信息。分析比較了不同崗位的薪資、學歷要求;分析比較了不同區域、行業對相關人才的需求情況;分析比較了不同崗位的知識、技能要求等。

做完以後的項目效果如下:

動態效果如下:

2、信息的爬取

( 基於51job招聘網站的數據爬取)

  • 爬取崗位:大數據、數據分析、機器學習、人工智能等相關崗位;

  • 爬取字段:公司名、崗位名、工作地址、薪資、發佈時間、工作描述、公司類型、員工人數、所屬行業;

  • 說明:基於51job招聘網站,我們搜索全國對於“數據”崗位的需求,大概有2000頁。我們爬取的字段,既有一級頁面的相關信息,還有二級頁面的部分信息;

  • 爬取思路:先針對某一頁數據的一級頁面做一個解析,然後再進行二級頁面做一個解析,最後再進行翻頁操作;

  • 使用工具:Python+requests+lxml+pandas+time

  • 網站解析方式:Xpath

1)導入相關庫

import requests
import pandas as pd
from pprint import pprint
from lxml import etree
import time
import warnings
warnings.filterwarnings("ignore")
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

2)關於翻頁的說明

# 第一頁的特點
https://search.51job.com/list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,1.html?
# 第二頁的特點
https://search.51job.com/list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,2.html?
# 第三頁的特點
https://search.51job.com/list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE,2,3.html?
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

注意:通過對於頁面的觀察,可以看出,就一個地方的數字變化了,因此只需要做字符串拼接,然後循環爬取即可。

3)完整的爬取代碼

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) + "頁的數據")
    url_pre = "https://search.51job.com/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、發佈日期
    release_time = dom.xpath('//div[@class="dw_table"]/div[@class="el"]/span[@class="t5"]/text()')
    # 6、獲取二級網址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、爬取經驗、學歷信息,先合在一個字段裏面,以後再做數據清洗。命名爲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、公司規模(人數)
        company_size = dom_test.xpath('//div[@class="tCompany_sidebar"]//div[@class="com_tag"]/p[2]/@title')
        # 11、所屬行業(公司)
        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)
        # 爲了反爬,設置睡眠時間
        time.sleep(1)
    # 由於我們需要爬取很多頁,爲了防止最後一次性保存所有數據出現的錯誤,因此,我們每獲取一夜的數據,就進行一次數據存取。
    df = pd.DataFrame()
    df["崗位名稱"] = job_name
    df["公司名稱"] = company_name
    df["工作地點"] = address
    df["工資"] = salary
    df["發佈日期"] = release_time
    df["經驗、學歷"] = RandomAll
    df["公司類型"] = CompanyType
    df["公司規模"] = CompanySize
    df["所屬行業"] = Industry
    df["崗位描述"] = JobDescribe
    # 這裏在寫出過程中,有可能會寫入失敗,爲了解決這個問題,我們使用異常處理。
    try:
        df.to_csv("job_info.csv", mode="a+", header=None, index=None, encoding="gbk")
    except:
        print("當頁數據寫入失敗")
    time.sleep(1)
print("數據爬取完畢,是不是很開心!!!")
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

這裏可以看到,我們爬取了1000多頁的數據做最終的分析。因此每爬取一頁的數據,做一次數據存儲,避免最終一次性存儲導致失敗。同時根據自己的測試,有一些頁數進行數據存儲,會導致失敗,爲了不影響後面代碼的執行,我們使用了“try-except”異常處理。

在一級頁面中,我們爬取了“崗位名稱”,“公司名稱”,“工作地點”,“工資”,“發佈日期”,“二級網址的url”這幾個字段。

在二級頁面中,我們爬取了“經驗、學歷信息”,“崗位描述”,“公司類型”,“公司規模”,“所屬行業”這幾個字段。

3、數據預處理

從爬取到的數據中截取部分做了一個展示,可以看出數據很亂。雜亂的數據並不利於我們的分析,因此需要根據研究的目標做一個數據預處理,得到我們最終可以用來做可視化展示的數據。

1)相關庫的導入及數據的讀取

 
df = pd.read_csv(r"G:\8泰迪\python_project\51_job\job_info1.csv",engine="python",header=None)
# 爲數據框指定行索引
df.index = range(len(df))
# 爲數據框指定列索引
df.columns = ["崗位名","公司名","工作地點","工資","發佈日期","經驗與學歷","公司類型","公司規模","行業","工作描述"]
2)數據去重
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

 

  • 我們認爲一個公司的公司名和和發佈的崗位名一致,就看作是重複值。因此,使用drop_duplicates(subset=[])函數,基於“崗位名”和“公司名”做一個重複值的剔除。

# 去重之前的記錄數
print("去重之前的記錄數",df.shape)
# 記錄去重
df.drop_duplicates(subset=["公司名","崗位名"],inplace=True)
# 去重之後的記錄數
print("去重之後的記錄數",df.shape)

3)崗位名字段的處理

① 崗位名字段的探索

df["崗位名"].value_counts()
df["崗位名"] = df["崗位名"].apply(lambda x:x.lower())
  • 說明:首先我們對每個崗位出現的頻次做一個統計,可以看出“崗位名字段”太雜亂,不便於我們做統計分析。接着我們將崗位名中的大寫英文字母統一轉換爲小寫字母,也就是說“AI”和“Ai”屬於同一個東西。

② 構造想要分析的目標崗位,做一個數據篩選

job_info.shape
target_job = ['算法', '開發', '分析', '工程師', '數據', '運營', '運維']
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
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 說明:首先我們構造瞭如上七個目標崗位的關鍵字眼。然後利用count()函數統計每一條記錄中,是否包含這七個關鍵字眼,如果包含就保留這個字段,不過不包含就刪除這個字段。最後查看篩選之後還剩餘多少條記錄。

③ 目標崗位標準化處理(由於目標崗位太雜亂,我們需要統一一下)

job_list = ['數據分析', "數據統計","數據專員",'數據挖掘', '算法',
            '大數據','開發工程師', '運營', '軟件工程', '前端開發',
            '深度學習', 'ai', '數據庫', '數據庫', '數據產品',
            '客服', 'java', '.net', 'andrio', '人工智能', 'c++',
            '數據管理',"測試","運維"]
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()
# 數據統計、數據專員、數據分析統一歸爲數據分析
job_info["崗位名"] = job_info["崗位名"].apply(lambda x:re.sub("數據專員","數據分析",x))
job_info["崗位名"] = job_info["崗位名"].apply(lambda x:re.sub("數據統計","數據分析",x))
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 說明:首先我們定義了一個想要替換的目標崗位job_list,將其轉換爲ndarray數組。然後定義一個函數,如果某條記錄包含job_list數組中的某個關鍵詞,那麼就將該條記錄替換爲這個關鍵詞,如果某條記錄包含job_list數組中的多個關鍵詞,我們只取第一個關鍵詞替換該條記錄。接着使用value_counts()函數統計一下替換後的各崗位的頻次。最後,我們將“數據專員”、“數據統計”統一歸爲“數據分析”。

4)工資水平字段的處理

工資水平字段的數據類似於“20-30萬/年”、“2.5-3萬/月”和“3.5-4.5千/月”這樣的格式。我們需要做一個統一的變化,將數據格式轉換爲“元/月”,然後取出這兩個數字,求一個平均值。

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)
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 說明:首先我們做了一個數據篩選,針對於每一條記錄,如果最後一個字在“年”和“月”中,同時第三個字在“萬”和“千”中,那麼就保留這條記錄,否則就刪除。接着定義了一個函數,將格式統一轉換爲“元/月”。最後將最低工資和最高工資求平均值,得到最終的“工資水平”字段。

5)工作地點字段的處理

由於整個數據是關於全國的數據,涉及到的城市也是特別多。我們需要自定義一個常用的目標工作地點字段,對數據做一個統一處理。

#job_info["工作地點"].value_counts()
address_list = ['北京', '上海', '廣州', '深圳', '杭州', '蘇州', '長沙',
                '武漢', '天津', '成都', '西安', '東莞', '合肥', '佛山',
                '寧波', '南京', '重慶', '長春', '鄭州', '常州', '福州',
                '瀋陽', '濟南', '寧波', '廈門', '貴州', '珠海', '青島',
                '中山', '大連','崑山',"惠州","哈爾濱","昆明","南昌","無錫"]
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)
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 說明:首先我們定義了一個目標工作地點列表,將其轉換爲ndarray數組。接着定義了一個函數,將原始工作地點記錄,替換爲目標工作地點中的城市。

6)公司類型字段的處理

這個很容易,就不詳細說明了。

job_info.loc[job_info["公司類型"].apply(lambda x:len(x)<6),"公司類型"] = np.nan
job_info["公司類型"] = job_info["公司類型"].str[2:-2]
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼】

7)行業字段的處理

每個公司的行業字段可能會有多個行業標籤,但是我們默認以第一個作爲該公司的行業標籤。

# job_info["行業"].value_counts()
job_info["行業"] = job_info["行業"].apply(lambda x:re.sub(",","/",x))
job_info.loc[job_info["行業"].apply(lambda x:len(x)<6),"行業"] = np.nan
job_info["行業"] = job_info["行業"].str[2:-2].str.split("/").str[0]
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

8)經驗與學歷字段的處理

關於這個字段的數據處理,我很是思考了一會兒,不太好敘述,放上代碼自己下去體會。

job_info["學歷"] = job_info["經驗與學歷"].apply(lambda x:re.findall("本科|大專|應屆生|在校生|碩士",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["學歷"] = job_info["學歷"].apply(func)

9)工作描述字段的處理

對於每一行記錄,我們去除停用詞以後,做一個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
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

10)公司規模字段的處理

#job_info["公司規模"].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["公司規模"] = job_info["公司規模"].apply(func)
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

11)構造新數據

我們針對最終清洗乾淨的數據,選取需要分析的字段,做一個數據存儲。

feature = ["公司名","崗位名","工作地點","工資水平","發佈日期","學歷","公司類型","公司規模","行業","工作描述"]
final_df = job_info[feature]
final_df.to_excel(r"G:\8泰迪\python_project\51_job\詞雲圖.xlsx",encoding="gbk",index=None)

4、關於“工作描述”字段的特殊處理

由於我們之後需要針對不同的崗位名做不同的詞雲圖處理,並且是在tableau中做可視化展示,因此我們需要按照崗位名分類,求出不同崗位下各關鍵詞的詞頻統計。

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 = ['數據分析', '算法', '大數據','開發工程師', '運營', '軟件工程','運維', '數據庫','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")

5、tableau可視化展示

1) 熱門城市的用人需求TOP10

2)熱門城市的崗位數量TOP10

3)不同工作地點崗位數量的氣泡圖

4)熱門崗位的薪資待遇

5)熱門行業的薪資待遇

6)可視化大屏的最終展示

7)可視化大屏的“動態”展示

說明:這裏最終就不做結論分析了,因爲結論通過上圖,就可以很清晰的看出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章