主頁 > 後端開發 > 嘔心瀝血的萬字長文,Python采集崗位資料,Tableau可視化分析

嘔心瀝血的萬字長文,Python采集崗位資料,Tableau可視化分析

2021-09-29 06:28:34 後端開發

一、專案背景

隨著科技的不斷進步與發展,資料呈現爆發式的增長,各行各業對于資料的依賴越來越強,與資料打交道在所難免,而社會對于“資料”方面的人才需求也在不斷增大,因此了解當下企業究竟需要招聘什么樣的人才?需要什么樣的技能?不管是對于在校生,還是對于求職者來說,都顯得十分必要,

很多人學習python,不知道從何學起,

很多人學習python,掌握了基本語法過后,不知道在哪里尋找案例上手,

很多已經做案例的人,卻不知道如何去學習更加高深的知識,

那么針對這三類人,我給大家提供一個好的學習平臺,免費領取視頻教程,電子書籍,以及課程的源代碼!

QQ群:701698587

歡迎加入,一起討論 一起學習!

  

對于一名小白來說,想要入門資料分析,首先要了解目前社會對于資料相關崗位的需求情況,基于這一問題,本文針對前程無憂招聘網站,利用python爬取了其全國范圍內大資料、資料分析、資料挖掘、機器學習、人工智能等與資料相關的崗位招聘資訊,并通過Tableau可視化工具分析比較了不同行業的崗位薪資、用人需求等情況;以及不同行業、崗位的知識、技能要求等,

可視化分析效果圖示例:

 

二、資料爬取

爬取欄位:崗位名稱、公司名稱、薪資水平、作業經驗、學歷需求、作業地點、招聘人數、發布時間、公司型別、公司規模、行業領域、福利待遇、職位資訊;

說明:在前程無憂招聘網站中,我們在搜索框中輸入“資料”兩個字進行搜索發現,共有2000個一級頁面,其中每個頁面包含50條崗位資訊,因此總共有約100000條招聘資訊,當點擊一級頁面中每個崗位資訊時,頁面會跳轉至相應崗位的二級頁面,二級頁面中即包含我們所需要的全部欄位資訊;

一級頁面如下:

 

二級頁面如下:

 

  • 爬取思路:先針對一級頁面爬取所有崗位對應的二級頁面鏈接,再根據二級頁面鏈接遍歷爬取相應崗位資訊;
  • 開發環境:python3、Spyder

1、相關庫的匯入與說明

import json
import requests
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from lxml import etree
from selenium.webdriver import ChromeOptions

由于前程無憂招聘網站的反爬機制較強,采用動態渲染+限制ip訪問頻率等多層反爬,因此在獲取二級頁面鏈接時需借助json進行決議,本文對于二級頁面崗位資訊的獲取采用selenium模擬瀏覽器爬取,同時通過代理IP的方式,每隔一段時間換一次請求IP以免觸發網站反爬機制,

2、獲取二級頁面鏈接

1)分析一級頁面url特征

# 第一頁URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,資料,2,1.html?"
# 第二頁URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,資料,2,2.html?"
# 第三頁URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,資料,2,3.html?"

通過觀察不同頁面的URL可以發現,不同頁面的URL鏈接只有“.html”前面的數字不同,該數字正好代表該頁的頁碼 ,因此只需要構造字串拼接,然后通過for回圈陳述句即可構造自動翻頁,

2)構建一級url庫

url1 = []
for i in range(2000):
    url_pre = "https://search.51job.com/list/000000,000000,0000,00,9,99,資料,2,%s" % (1+i) #設定自動翻頁   
    url_end = ".html?"
    url_all = url_pre + url_end
    url1.append(url_all)
print("一級URL庫創建完畢")

3)爬取所有二級url鏈接

url2 = []
j = 0
for url in url1:
    j += 1
    re1 = requests.get(url , headers = headers,proxies= {'http':'tps131.kdlapi.com:15818'},timeout=(5,10))  #通過proxies設定代理ip
    html1 = etree.HTML(re1.text)    
    divs = html1.xpath('//script[@type = "text/javascript"]/text()')[0].replace('window.__SEARCH_RESULT__ = ',"")    
    js = json.loads(divs)
    for i in range(len(js['engine_jds'])):
        if js['engine_jds'][i]['job_href'][0:22] == "https://jobs.51job.com":
            url2.append(js['engine_jds'][i]['job_href'])
        else:
            print("url例外,棄用")  #剔除例外url
    print("已爬取"+str(j)+"頁")
print("成功爬取"+str(len(url2))+"條二級URL")

注意:爬取二級URL鏈接時發現并非爬取的所有鏈接都是規范的,會存在少部分例外URL,這會對后續崗位資訊的爬取造成干擾,因此需要利用if條件陳述句對其進行剔除,

3、獲取崗位資訊并保存

option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_argument('--proxy-server=http://tps131.kdlapi.com:15818')  #設定代理ip
driver = webdriver.Chrome(options=option)
for url in url2:
    co = 1
    while co == 1:
        try:
            driver.get(url)
            wait = WebDriverWait(driver,10,0.5)
            wait.until(EC.presence_of_element_located((By.ID,'topIndex')))
        except:
            driver.close()
            driver = webdriver.Chrome(options=option)
            co = 1
        else:
            co = 0
    try:
        福利待遇 = driver.find_elements_by_xpath('//div[@class = "t1"]')[0].text 
        崗位名稱 = driver.find_element_by_xpath('//div[@class = "cn"]/h1').text
        薪資水平 = driver.find_element_by_xpath('//div[@class = "cn"]/strong').text
        職位資訊 = driver.find_elements_by_xpath('//div[@class = "bmsg job_msg inbox"]')[0].text
        公司型別 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[0].text
        公司規模 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[1].text
        公司領域 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[2].text
        公司名稱 = driver.find_element_by_xpath('//div[@class = "com_msg"]/a/p').text
        作業地點 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[0]
        作業經驗 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[1]
        學歷要求 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[2]
        招聘人數 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[3]
        發布時間 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[4]
    except:
        福利待遇 = "nan" 
        崗位名稱 = "nan"
        薪資水平 = "nan"
        職位資訊 = "nan"
        公司型別 = "nan"
        公司規模 = "nan"
        公司領域 = "nan"
        公司名稱 = "nan"
        作業地點 = "nan"
        作業經驗 = "nan"
        學歷要求 = "nan"
        招聘人數 = "nan"
        發布時間 = "nan"
        print("資訊提取例外,棄用")
    finally:     
        info = {    
            "崗位名稱" : 崗位名稱,
            "公司名稱" : 公司名稱,
            "薪資水平" : 薪資水平,
            "作業經驗" : 作業經驗,
            "學歷要求" : 學歷要求,
            "作業地點" : 作業地點,
            "招聘人數" : 招聘人數,
            "發布時間" : 發布時間,
            "公司型別" : 公司型別,
            "公司規模" : 公司規模,
            "公司領域" : 公司領域,
            "福利待遇" : 福利待遇,
            "職位資訊" : 職位資訊
            }
        jobs_info.append(info)
df = pd.DataFrame(jobs_info)
df.to_excel(r"E:\python爬蟲\前程無憂招聘資訊.xlsx") 

在爬取并剔除例外資料之后,最終得到了90000多條完整的資料做分析,但經過觀察發現,所爬取的資料并非全都與“資料”崗位相關聯,實際上,前程無憂招聘網站上與“資料”有關的只有幾百頁,而我們爬取了2000頁的所有資料,因此在后面進行資料處理時需要把無關的資料剔除掉,在爬取前根據對代碼的測驗發現,有些崗位欄位在進行爬取時會出現錯位,從而導致資料存盤失敗,為了不影響后面代碼的執行,這里設定了“try-except”進行例外處理,同時使用while回圈陳述句在服務器出現請求失敗時關閉模擬瀏覽器并進行重新請求,

三、資料清洗

1、資料讀取、去重、空值處理

在獲取了所需資料之后,可以看出資料較亂,并不利于我們進行分析,因此在分析前需要對資料進行預處理,得到規范格式的資料才可以用來最終做可視化資料展示,

獲取的資料截圖如下:

 

1)相關庫匯入及資料讀取

#匯入相關庫
import pandas as pd
import numpy as np
import jieba
 
#讀取資料
df = pd.read_excel(r'E:\python爬蟲\前程無憂招聘資訊.xlsx',index_col=0)

2)資料去重與控制處理

  • 對于重復值的定義,我們認為一個記錄的公司名稱和崗位名稱一致時,即可看作是重復值,因此利用drop_duplicates()函式剔除所有公司名稱和崗位名稱相同的記錄并保留第一個記錄,
  • 對于空值處理,只洗掉所有欄位資訊都為nan的記錄,
#去除重復資料
df.drop_duplicates(subset=['公司名稱','崗位名稱'],inplace=True)
 
#空值洗掉
df[df['公司名稱'].isnull()]
df.dropna(how='all',inplace=True)

2、“崗位名稱”欄位預處理

1)”崗位名稱“欄位預覽

首先我們對“崗位名稱”的格式進行調整,將其中所有大寫英文字母統一轉換為小寫,例如將"Java"轉換為"java",然后對所有崗位做一個頻次統計,統計結果發現“崗位名稱”欄位很雜亂,且存在很多與“資料”無關的崗位,因此要對資料做一個篩選,

df['崗位名稱'] = df['崗位名稱'].apply(lambda x:x.lower())
counts = df['崗位名稱'].value_counts() 

2)構建關鍵詞,篩選名稱

首先我們列出與“資料”崗位“有關的一系列關鍵詞,然后通過count()與for陳述句對所有記錄進行統計判斷,如果包含任一關鍵詞則保留該記錄,如果不包含則洗掉該欄位,

#構建目標關鍵詞
target_job = ['演算法','開發','分析','工程師','資料','運營','運維','it','倉庫','統計']
#篩選目標資料
index = [df['崗位名稱'].str.count(i) for i in target_job]
index = np.array(index).sum(axis=0) > 0
job_info = df[index]

3)崗位名稱標準化處理

基于前面對“崗位名稱”欄位的統計情況,我們定義了目標崗位串列job_list,用來替換統一相近的崗位名稱,之后,我們將“資料專員”、“資料統計”統一歸為“資料分析”,

job_list = ['資料分析',"資料統計","資料專員",'資料挖掘','演算法','大資料','開發工程師','運營',
            '軟體工程','前端開發','深度學習','ai','資料庫','倉庫管理','資料產品','客服',
            'java','.net','andrio','人工智能','c++','資料管理',"測驗","運維","資料工程師"]
job_list = np.array(job_list)
def Rename(x,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["崗位名稱"] = job_info["崗位名稱"].apply(lambda x:x.replace("資料專員","資料分析"))
job_info["崗位名稱"] = job_info["崗位名稱"].apply(lambda x:x.replace("資料統計","資料分析"))

統一之后的“崗位名稱”如下圖所示:

 

3、“崗位薪資”欄位預處理

對于“崗位薪資”欄位的處理,重點在于對其單位格式轉換,在簡單觀察該欄位后發現,其存在“萬/年”、“萬/月”、“千/月”等不同單位,因此需要對其做一個統一換算,將資料格式統一轉換為“元/月”,并根據最高工資與最低工資求出平均值,

job_info['崗位薪資'].value_counts()
 
#剔除例外資料
index1 = job_info["崗位薪資"].str[-1].isin(["年","月"])
index2 = job_info["崗位薪資"].str[-3].isin(["萬","千"])
job_info = job_info[index1 & index2]
#計算平均工資
job_info['平均薪資'] = job_info['崗位薪資'].astype(str).apply(lambda x:np.array(x[:-3].split('-'),dtype=float))
job_info['平均薪資'] = job_info['平均薪資'].apply(lambda x:np.mean(x))
#統一工資單位
job_info['單位'] = job_info['崗位薪資'].apply(lambda x:x[-3:])
def con_unit(x):
    if x['單位'] == "萬/月":
        z = x['平均薪資']*10000
    elif x['單位'] == "千/月":
        z = x['平均薪資']*1000
    elif x['單位'] == "萬/年":
        z = x['平均薪資']/12*10000
    return int(z)
job_info['平均薪資'] = job_info.apply(con_unit,axis=1)
job_info['單位'] = '元/月'

說明:首先我們對該欄位進行統計預覽,之后做一個資料篩選剔除例外單位與空值記錄,再計算出每個欄位的平均工資,接著定義一個函式,將格式換算為“元/月”,得到最終的“平均薪資”欄位,

4、“公司規模”欄位預處理

對于“公司規模”欄位的處理較簡單,只需要定義一個if條件陳述句將其格式做一個轉換即可,

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)

5、“職位資訊”欄位預處理

job_info['職位資訊'] = job_info['職位資訊'].apply(lambda x:x.split('職能類別')[0])
with open(r"E:\python爬蟲\資料處理\停用詞表.txt",'r',encoding = 'utf8') as f:
    stopword = f.read()
stopword = stopword.split()
#對“職業資訊”欄位進行簡單處理,去除無意義的文字,構造jieba分詞
job_info['職位資訊'] = job_info['職位資訊'].apply(lambda x:x.lower()).apply(lambda x:"".join(x)).apply(lambda x:x.strip()).apply(jieba.lcut).apply(lambda x:[i for i in x if i not in stopword])
#按照行業進行分類,求出每一個行業下各關鍵詞的詞頻統計,以便于后期做詞云圖
cons = job_info['公司領域'].value_counts()
industries = pd.DataFrame(cons.index,columns=['行業領域'])
industry = pd.DataFrame(columns=['分詞明細','行業領域'])
for i in industries['行業領域']:
    words = []
    word = job_info['職位資訊'][job_info['公司領域'] == i]
    word.dropna(inplace=True)
    [words.extend(str(z).strip('\'[]').split("\', \'")) for z in word]
    df1 = pd.DataFrame({'分詞明細':words,
                        '行業領域':i})
    industry = industry.append(df1,ignore_index=True)
industry = industry[industry['分詞明細'] != "\\n"]
industry = industry[industry['分詞明細'] != ""]
#剔除詞頻小于300的關鍵詞
count = pd.DataFrame(industry['分詞明細'].value_counts())
lst = list(count[count['分詞明細'] >=300].index)
industry = industry[industry['分詞明細'].isin(lst)]
#資料存盤
industry.to_excel(r'E:\python爬蟲\資料處理\詞云.xlsx')   

6、其它欄位預處理

“作業地點”欄位:該欄位有”市-區“和”市“兩種格式,如”廣州-天河“與”廣州“,因此需要統一轉換為”市“的格式;

“公司領域”欄位:每個公司的行業欄位可能會有多個行業標簽,我們默認以第一個作為改公司的行業標簽;

“招聘人數”欄位:由于某些公司崗位沒有具體招聘人數,因此我們默認以最低需求為標準,將“招若干人”改為“招1人”,以便于后面統計分析;

其它欄位:對于其他幾個欄位格式只存在一些字串空格問題,因此只需要對其進行去除空格即可,

#作業地點欄位處理
job_info['作業地點'] = job_info['作業地點'].apply(lambda x:x.split('-')[0])
 
#公司領域欄位處理
job_info['公司領域'] = job_info['公司領域'].apply(lambda x:x.split('/')[0])
a = job_info['公司領域'].value_counts()
 
#招聘人數欄位處理
job_info['招聘人數'] = job_info['招聘人數'].apply(lambda x:x.replace("若干","1").strip()[1:-1])
 
#作業經驗與學歷要求欄位處理
job_info['作業經驗'] = job_info['作業經驗'].apply(lambda x:x.replace("無需","1年以下").strip()[:-2])
job_info['學歷需求'] = job_info['學歷需求'].apply(lambda x:x.split()[0])
 
#公司福利欄位處理
job_info['公司福利'] = job_info['公司福利'].apply(lambda x:str(x).split())

7、資料存盤

我們針對清洗干凈后的資料另存為一個檔案,對源資料不做修改,

job_info.to_excel(r'E:\python爬蟲\前程無憂(已清洗).xlsx')

四、Tableau資料可視化展示

1、崗位數量城市分布氣泡圖

 

結論分析:從氣泡圖中可以看出,“資料”相關崗位數量較高的城市有:上海、深圳、廣州、北京、杭州、武漢等,

2、熱門城市用人需求Top15

 

結論分析:通過條形圖可以看出,“資料”相關崗位用人需求達1000人以上的城市有15個,需求由高到低依次為:上海、深圳、廣州、北京、武漢、杭州、成都、南京、蘇州、無錫、西安、長沙、鄭州、重慶,其中上海用人需求高達10000人,

3、用人需求Top15行業及其薪資情況

 

結論分析:從不同行業的用人需求與薪資對比可知,用人需求排名前4的行業分別:計算機軟體、互聯網、電子技術、計算機服務;平均薪資排名前4的行業分別為:互聯網、計算機軟體、通信、專業服務,可以發現,“資料”相關崗位在計算機領域需求大,薪資高,前景好,

4、各型別企業崗位需求樹狀分布圖

 

結論分析:在發布的眾多崗位需求資訊中,以民營公司為主,其崗位數量、用人需求極高,但薪資待遇一般,而上市公司的崗位數量一般,但薪資待遇好,

5、經驗學歷與薪資需求突出顯示表

注:顏色深淺表示薪資高低,數字表示招聘人數

 

結論分析:根據突出顯示表可以發現,在學歷要求方面,大專與本科生需求量較大;經驗要求方面,3年以下相關經驗的崗位占大多數,而薪資方面,學歷越高,經驗越豐富則薪資越高,因此可以判斷資料分析行業還是一個較新興的行業,目前行業的基礎崗位較多,且具有豐富經驗的專家較少,

6、不同行業知識、技能要求詞云圖

1)傳統制造業

 

2) 計算機相關行業

 

3)服務行業

 

結論分析:上圖通過列舉了傳統制造業、計算機相關行業以及服務業三個行業進行對比分析,三個行業對于“資料”相關崗位作業要求的共同點都是注重相關的行業經驗及資料處理等能力,而計算機相關行業對于技術如開發、資料庫、系統維護等編程能力要求較高,傳統制造業和服務行業則更側重于業務分析、管理、團隊合作綜合型能力等,

6、崗位數量與薪資水平地理分布

 

7、可視化看板最終展示結果

 

 

五、源代碼

1、爬蟲源代碼

import json
import requests
import pandas as pd
from lxml import etree
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ChromeOptions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
url1 = []
url2 = []
jobs_info = []
for i in range(2000):
    url_pre = "https://search.51job.com/list/000000,000000,0000,00,9,99,資料,2,%s" % (1+i)    #頁面跳轉
    url_end = ".html?"
    url_all = url_pre + url_end
    url1.append(url_all)
print("一級URL庫創建完畢")
 
#從json中提取資料并加載
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36',
           'Connection': 'close',
           'Host': 'search.51job.com'}
j = 0
for url in url1:
    j += 1
    re1 = requests.get(url , headers = headers,proxies= {'http':'tps131.kdlapi.com:15818'},timeout=(5,10))
    html1 = etree.HTML(re1.text)    
    divs = html1.xpath('//script[@type = "text/javascript"]/text()')[0].replace('window.__SEARCH_RESULT__ = ',"")    
    js = json.loads(divs)
    for i in range(len(js['engine_jds'])):
        if js['engine_jds'][i]['job_href'][0:22] == "https://jobs.51job.com":
            url2.append(js['engine_jds'][i]['job_href'])
        else:
            print("url例外,棄用")
    print("已決議"+str(j)+"頁")
print("成功提取"+str(len(url2))+"條二級URL")
 
#爬取崗位資料
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_argument('--proxy-server=http://tps131.kdlapi.com:15818') 
driver = webdriver.Chrome(options=option)
for url in url2:
    co = 1
    while co == 1:
        try:
             #設定IP代理
            driver.get(url)
            wait = WebDriverWait(driver,10,0.5)
            wait.until(EC.presence_of_element_located((By.ID,'topIndex')))
        except:
            driver.close()
            driver = webdriver.Chrome(options=option)
            co = 1
        else:
            co = 0
    try:
        福利待遇 = driver.find_elements_by_xpath('//div[@class = "t1"]')[0].text 
        崗位名稱 = driver.find_element_by_xpath('//div[@class = "cn"]/h1').text
        薪資水平 = driver.find_element_by_xpath('//div[@class = "cn"]/strong').text
        職位資訊 = driver.find_elements_by_xpath('//div[@class = "bmsg job_msg inbox"]')[0].text
        公司型別 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[0].text
        公司規模 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[1].text
        公司領域 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[2].text
        公司名稱 = driver.find_element_by_xpath('//div[@class = "com_msg"]/a/p').text
        作業地點 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[0]
        作業經驗 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[1]
        學歷要求 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[2]
        招聘人數 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[3]
        發布時間 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[4]
    except:
        福利待遇 = "nan" 
        崗位名稱 = "nan"
        薪資水平 = "nan"
        職位資訊 = "nan"
        公司型別 = "nan"
        公司規模 = "nan"
        公司領域 = "nan"
        公司名稱 = "nan"
        作業地點 = "nan"
        作業經驗 = "nan"
        學歷要求 = "nan"
        招聘人數 = "nan"
        發布時間 = "nan"
        print("資訊提取例外,棄用")
    finally:     
        info = {    
            "崗位名稱" : 崗位名稱,
            "公司名稱" : 公司名稱,
            "薪資水平" : 薪資水平,
            "作業經驗" : 作業經驗,
            "學歷要求" : 學歷要求,
            "作業地點" : 作業地點,
            "招聘人數" : 招聘人數,
            "發布時間" : 發布時間,
            "公司型別" : 公司型別,
            "公司規模" : 公司規模,
            "公司領域" : 公司領域,
            "福利待遇" : 福利待遇,
            "職位資訊" : 職位資訊
            }
        jobs_info.append(info)
df = pd.DataFrame(jobs_info)
df.to_excel(r"E:\python爬蟲\前程無憂招聘資訊.xlsx") 

2、資料預處理原始碼

import pandas as pd
import numpy as np
import jieba
 
#資料讀取
df = pd.read_excel(r'E:\python爬蟲\前程無憂招聘資訊.xlsx',index_col=0)
 
#資料去重與空值處理
df.drop_duplicates(subset=['公司名稱','崗位名稱'],inplace=True)
df[df['招聘人數'].isnull()]
df.dropna(how='all',inplace=True)
 
#崗位名稱欄位處理
df['崗位名稱'] = df['崗位名稱'].apply(lambda x:x.lower())
counts = df['崗位名稱'].value_counts() 
target_job = ['演算法','開發','分析','工程師','資料','運營','運維','it','倉庫','統計']
index = [df['崗位名稱'].str.count(i) for i in target_job]
index = np.array(index).sum(axis=0) > 0
job_info = df[index]
job_list = ['資料分析',"資料統計","資料專員",'資料挖掘','演算法','大資料','開發工程師',
            '運營','軟體工程','前端開發','深度學習','ai','資料庫','倉庫管理','資料產品',
            '客服','java','.net','andrio','人工智能','c++','資料管理',"測驗","運維","資料工程師"]
job_list = np.array(job_list)
def Rename(x,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["崗位名稱"] = job_info["崗位名稱"].apply(lambda x:x.replace("資料專員","資料分析"))
job_info["崗位名稱"] = job_info["崗位名稱"].apply(lambda x:x.replace("資料統計","資料分析"))
 
#崗位薪資欄位處理
index1 = job_info["崗位薪資"].str[-1].isin(["年","月"])
index2 = job_info["崗位薪資"].str[-3].isin(["萬","千"])
job_info = job_info[index1 & index2]
job_info['平均薪資'] = job_info['崗位薪資'].astype(str).apply(lambda x:np.array(x[:-3].split('-'),dtype=float))
job_info['平均薪資'] = job_info['平均薪資'].apply(lambda x:np.mean(x))
#統一工資單位
job_info['單位'] = job_info['崗位薪資'].apply(lambda x:x[-3:])
job_info['公司領域'].value_counts()
def con_unit(x):
    if x['單位'] == "萬/月":
        z = x['平均薪資']*10000
    elif x['單位'] == "千/月":
        z = x['平均薪資']*1000
    elif x['單位'] == "萬/年":
        z = x['平均薪資']/12*10000
    return int(z)
job_info['平均薪資'] = job_info.apply(con_unit,axis=1)
job_info['單位'] = '元/月'
 
#作業地點欄位處理
job_info['作業地點'] = job_info['作業地點'].apply(lambda x:x.split('-')[0])
 
#公司領域欄位處理
job_info['公司領域'] = job_info['公司領域'].apply(lambda x:x.split('/')[0])
 
#招聘人數欄位處理
job_info['招聘人數'] = job_info['招聘人數'].apply(lambda x:x.replace("若干","1").strip()[1:-1])
 
#作業經驗與學歷要求欄位處理
job_info['作業經驗'] = job_info['作業經驗'].apply(lambda x:x.replace("無需","1年以下").strip()[:-2])
job_info['學歷需求'] = job_info['學歷需求'].apply(lambda x:x.split()[0])
 
#公司規模欄位處理
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)
 
#公司福利欄位處理
job_info['公司福利'] = job_info['公司福利'].apply(lambda x:str(x).split())
 
#職位資訊欄位處理
job_info['職位資訊'] = job_info['職位資訊'].apply(lambda x:x.split('職能類別')[0])
with open(r"E:\C++\停用詞表.txt",'r',encoding = 'utf8') as f:
    stopword = f.read()
stopword = stopword.split()
job_info['職位資訊'] = job_info['職位資訊'].apply(lambda x:x.lower()).apply(lambda x:"".join(x)).apply(lambda x:x.strip()).apply(jieba.lcut).apply(lambda x:[i for i in x if i not in stopword])
cons = job_info['公司領域'].value_counts()
industries = pd.DataFrame(cons.index,columns=['行業領域'])
industry = pd.DataFrame(columns=['分詞明細','行業領域'])
for i in industries['行業領域']:
    words = []
    word = job_info['職位資訊'][job_info['公司領域'] == i]
    word.dropna(inplace=True)
    [words.extend(str(z).strip('\'[]').split("\', \'")) for z in word]
    df1 = pd.DataFrame({'分詞明細':words,
                        '行業領域':i})
    industry = industry.append(df1,ignore_index=True)
industry = industry[industry['分詞明細'] != "\\n"]
industry = industry[industry['分詞明細'] != ""]
count = pd.DataFrame(industry['分詞明細'].value_counts())
lst = list(count[count['分詞明細'] >=300].index)
industry = industry[industry['分詞明細'].isin(lst)]
 
#資料存盤
industry.to_excel(r'E:\python爬蟲\資料預處理\詞云.xlsx')       
job_info.to_excel(r'E:\python爬蟲\資料預處理\前程無憂(已清洗).xlsx')

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/303724.html

標籤:其他

上一篇:低版本spring-cloud-sleuth的TraceableExecutorService一個坑,注意繞行

下一篇:Python代碼閱讀(第8篇):串列元素邏輯判斷

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more