python爬蟲:爬取拉勾網職位並分析

前言

文的文字及圖片來源於網絡,僅供學習、交流使用,不具備任何商業用途,版權歸原做者全部,若有問題請及時聯繫咱們以做處理。python

做者: 閒庭信步正則表達式

源自:http://www.javashuo.com/article/p-nlgioage-dk.htmljson

PS:若有須要Python學習資料的小夥伴能夠加點擊下方連接自行獲取api

http://note.youdao.com/noteshare?id=3054cce4add8a909e784ad934f956cef 數組

0. 前言

本文從拉勾網爬取深圳市數據分析的職位信息,並以CSV格式保存至電腦,以後進行數據清洗,生成詞雲,進行描述統計和迴歸分析,最終得出結論。服務器

1. 用到的軟件包

  • Python版本: Python3.6 網絡

  • requests:下載網頁架構

  • math:向上取整app

  • time:暫停進程機器學習

  • pandas:數據分析並保存爲csv文件

  • matplotlib:畫圖

  • statsmodels:統計建模

  • wordcloud、scipy、jieba:生成中文詞雲

  • pylab:設置畫圖能顯示中文

2. 解析網頁

打開Chrome,在拉勾網搜索深圳市的「數據分析」職位,使用檢查功能查看網頁源代碼,發現拉勾網有反爬蟲機制,職位信息並不在源代碼裏,而是保存在JSON的文件裏,所以咱們直接下載JSON,並使用字典方法直接讀取數據。 在這裏插入圖片描述

抓取網頁時,須要加上頭部信息,才能獲取所需的數據。

def get_json(url,num):  
   '''''從網頁獲取JSON,使用POST請求,加上頭部信息'''  
   my_headers = {  
           'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',  
           'Host':'www.lagou.com',  
           'Referer':'https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?labelWords=&fromSearch=true&suginput=',  
           'X-Anit-Forge-Code':'0',  
           'X-Anit-Forge-Token': 'None',  
           'X-Requested-With':'XMLHttpRequest'  
           }  
​
   my_data = {  
           'first': 'true',  
           'pn':num,  
           'kd':'數據分析'}  
​
   res = requests.post(url, headers = my_headers, data = my_data)  
   res.raise_for_status()  
   res.encoding = 'utf-8'  
   # 獲得包含職位信息的字典  
   page = res.json()  
   return page

 

在搜索結果的第一頁,咱們能夠從JSON裏讀取總職位數,按照每頁15個職位,得到要爬取的頁數。再使用循環按頁爬取,將職位信息彙總,輸出爲CSV格式。

程序運行如圖:

在這裏插入圖片描述

抓取結果如圖:

在這裏插入圖片描述

3. 數據清洗

數據清洗佔數據分析工做量的大頭。在拉勾網搜索深圳市的「數據分析」職位,結果獲得369個職位。查看職位名稱時,發現有4個實習崗位。因爲咱們研究的是全職崗位,因此先將實習崗位剔除。因爲工做經驗和工資都是字符串形式的區間,咱們先用正則表達式提取數值,輸出列表形式。工做經驗取均值,工資取區間的四分位數值,比較接近現實。

# 數據清洗,剔除實習崗位  
df.drop(df[df['職位名稱'].str.contains('實習')].index, inplace=True)  
# print(df.describe())  
# 因爲CSV文件內的數據是字符串形式,先用正則表達式將字符串轉化爲列表,再取區間的均值  
pattern = '\d+'  
df['工做年限'] = df['工做經驗'].str.findall(pattern)  
​
avg_work_year = []  
for i in df['工做年限']:  
   # 若是工做經驗爲'不限'或'應屆畢業生',那麼匹配值爲空,工做年限爲0  
   if len(i) == 0:  
       avg_work_year.append(0)  
   # 若是匹配值爲一個數值,那麼返回該數值  
   elif len(i) == 1:  
       avg_work_year.append(int(''.join(i)))  
   # 若是匹配值爲一個區間,那麼取平均值  
   else:  
       num_list = [int(j) for j in i]  
       avg_year = sum(num_list)/2  
       avg_work_year.append(avg_year)  
​
df['經驗'] = avg_work_year  
​
# 將字符串轉化爲列表,再取區間的前25%,比較貼近現實  
df['salary'] = df['工資'].str.findall(pattern)  
​
avg_salary = []  
for k in df['salary']:  
   int_list = [int(n) for n in k]  
   avg_wage = int_list[0]+(int_list[1]-int_list[0])/4  
   avg_salary.append(avg_wage)  
​
df['月工資'] = avg_salary  
# 將清洗後的數據保存,以便檢查  
df.to_csv('draft.csv', index = False)

 

4. 詞雲

咱們將職位福利這一列的數據彙總,生成一個字符串,按照詞頻生成詞雲實現python可視化。如下是原圖和詞雲的對比圖,可見五險一金在職位福利裏出現的頻率最高,平臺、福利、發展空間、彈性工做次之。 在這裏插入圖片描述

5. 描述統計

在這裏插入圖片描述

可知,數據分析師的均值在14.6K,中位數在12.5K,算是較有前途的職業。數據分析散佈在各個行業,但在高級層面上涉及到數據挖掘和機器學習,在IT業有長足的發展。 咱們再來看工資的分佈,這對於求職來說是重要的參考:

在這裏插入圖片描述

工資在10-15K的職位最多,在15-20K的職位其次。我的愚見,10-15K的職位以建模爲主,20K以上的職位以數據挖掘、大數據架構爲主。 咱們再來看職位在各區的分佈: 在這裏插入圖片描述

數據分析職位有62.9%在南山區,有25.8%在福田區,剩下少數分佈在龍崗區、羅湖區、寶安區、龍華新區。咱們以小窺大,可知南山區和福田區是深圳市科技業的中心。

6. 實證統計

咱們但願得到工資與工做經驗、學歷的關係,因爲學歷分三類,需設置3個虛擬變量:大專、本科、碩士。多元迴歸結果以下:

在這裏插入圖片描述

在0.05的顯著性水平下,F值爲82.53,說明迴歸關係是顯著的。t檢驗和對應的P值都小於0.05代表,工做經驗和3種學歷在統計上都是顯著的。另外,R-squared的值爲0.41,說明工做經驗和學歷僅僅解釋了工資變異性的41%。這點不難理解,即便職位都叫數據分析師,實際的工做內容差別比較大,有的只是用Excel作基本分析,有的用Python、R作數據挖掘。另外,各個公司的規模和它願意開出的工資也不盡相同。而工做內容的差別和公司的大方程度是很難單憑招聘網頁上的宣傳而得到實際數據,致使了模型的擬合優度不是很好這一現實。

因爲迴歸模型整體是顯著的,咱們能夠將自變量的值代入迴歸方程,得到各個學歷的工資的指望值E。對於數據分析職位,以1年工做經驗爲例,大專學歷的指望工資是7.8K,本科學歷的指望工資是10.8K,碩士學歷的指望工資是17.6K。這證明了「知識改變命運」這一說法。

7. 完整代碼

因爲每次運行爬蟲耗時約30分鐘,而運行數據分析耗時幾秒鐘,咱們將兩部分的工做單獨運行,以節省數據分析的時間。

7.1 爬蟲部分的代碼

 1 import requests  
 2 import math  
 3 import pandas as pd  
 4 import time  
 5 '''
 6 python學習交流羣:821460695更多學習資料能夠加羣獲取
 7 '''
 8 def get_json(url,num):  
 9    '''''從網頁獲取JSON,使用POST請求,加上頭部信息'''  
10    my_headers = {  
11            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',  
12            'Host':'www.lagou.com',  
13            'Referer':'https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?labelWords=&fromSearch=true&suginput=',  
14            'X-Anit-Forge-Code':'0',  
15            'X-Anit-Forge-Token': 'None',  
16            'X-Requested-With':'XMLHttpRequest'  
17            }  
18 19    my_data = {  
20            'first': 'true',  
21            'pn':num,  
22            'kd':'數據分析'}  
23 24    res = requests.post(url, headers = my_headers, data = my_data)  
25    res.raise_for_status()  
26    res.encoding = 'utf-8'  
27    # 獲得包含職位信息的字典  
28    page = res.json()  
29    return page  
30 31 32 def get_page_num(count):  
33    '''''計算要抓取的頁數'''  
34    # 每頁15個職位,向上取整  
35    res = math.ceil(count/15)  
36    # 拉勾網最多顯示30頁結果  
37    if res > 30:  
38        return 30  
39    else:  
40        return res  
41 42 def get_page_info(jobs_list):  
43    '''''對一個網頁的職位信息進行解析,返回列表'''  
44    page_info_list = []  
45    for i in jobs_list:  
46        job_info = []  
47        job_info.append(i['companyFullName'])  
48        job_info.append(i['companyShortName'])  
49        job_info.append(i['companySize'])  
50        job_info.append(i['financeStage'])  
51        job_info.append(i['district'])  
52        job_info.append(i['positionName'])  
53        job_info.append(i['workYear'])  
54        job_info.append(i['education'])  
55        job_info.append(i['salary'])  
56        job_info.append(i['positionAdvantage'])  
57        page_info_list.append(job_info)  
58    return page_info_list  
59 60 def main():  
61    url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E6%B7%B1%E5%9C%B3&needAddtionalResult=false'  
62    # 先設定頁數爲1,獲取總的職位數  
63    page_1 = get_json(url,1)  
64    total_count = page_1['content']['positionResult']['totalCount']  
65    num = get_page_num(total_count)  
66    total_info = []  
67    time.sleep(20)  
68    print('職位總數:{},頁數:{}'.format(total_count,num))  
69 70    for n in range(1,num+1):  
71        # 對每一個網頁讀取JSON, 獲取每頁數據  
72        page = get_json(url,n)  
73        jobs_list = page['content']['positionResult']['result']  
74        page_info = get_page_info(jobs_list)  
75        total_info += page_info  
76        print('已經抓取第{}頁, 職位總數:{}'.format(n, len(total_info)))  
77        # 每次抓取完成後,暫停一會,防止被服務器拉黑  
78        time.sleep(30)  
79    #將總數據轉化爲data frame再輸出  
80    df = pd.DataFrame(data = total_info,columns = ['公司全名','公司簡稱','公司規模','融資階段','區域','職位名稱','工做經驗','學歷要求','工資','職位福利'])   
81    df.to_csv('lagou_jobs.csv',index = False)  
82    print('已保存爲csv文件.')  
83 84 if __name__== "__main__":   
85    main()

 

7.2 數據分析部分的代碼

import pandas as pd  
import matplotlib.pyplot as plt  
import statsmodels.api as sm  
from wordcloud import WordCloud  
from scipy.misc import imread  
import jieba  
from pylab import mpl  
'''
python學習交流羣:821460695更多學習資料能夠加羣獲取
'''
# 使matplotlib模塊能顯示中文  
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體  
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示爲方塊的問題  
# 讀取數據  
df = pd.read_csv('lagou_jobs.csv', encoding = 'gbk')  
​
# 數據清洗,剔除實習崗位  
df.drop(df[df['職位名稱'].str.contains('實習')].index, inplace=True)  
# print(df.describe())  
# 因爲CSV文件內的數據是字符串形式,先用正則表達式將字符串轉化爲列表,再取區間的均值  
pattern = '\d+'  
df['工做年限'] = df['工做經驗'].str.findall(pattern)  
​
avg_work_year = []  
for i in df['工做年限']:  
   # 若是工做經驗爲'不限'或'應屆畢業生',那麼匹配值爲空,工做年限爲0  
   if len(i) == 0:  
       avg_work_year.append(0)  
   # 若是匹配值爲一個數值,那麼返回該數值  
   elif len(i) == 1:  
       avg_work_year.append(int(''.join(i)))  
   # 若是匹配值爲一個區間,那麼取平均值  
   else:  
       num_list = [int(j) for j in i]  
       avg_year = sum(num_list)/2  
       avg_work_year.append(avg_year)  
​
df['經驗'] = avg_work_year  
​
# 將字符串轉化爲列表,再取區間的前25%,比較貼近現實  
df['salary'] = df['工資'].str.findall(pattern)  
​
avg_salary = []  
for k in df['salary']:  
   int_list = [int(n) for n in k]  
   avg_wage = int_list[0]+(int_list[1]-int_list[0])/4  
   avg_salary.append(avg_wage)  
​
df['月工資'] = avg_salary  
# 將清洗後的數據保存,以便檢查  
df.to_csv('draft.csv', index = False)  
​
# 描述統計  
print('數據分析師工資描述:\n{}'.format(df['月工資'].describe()))  
​
# 繪製頻率直方圖並保存  
plt.hist(df['月工資'],bins = 12)  
plt.xlabel('工資 (千元)')   
plt.ylabel('頻數')   
plt.title("工資直方圖")   
plt.savefig('histogram.jpg')  
plt.show()  
​
# 繪製餅圖並保存  
count = df['區域'].value_counts()  
# 將龍華區和龍華新區的數據彙總  
count['龍華新區'] += count['龍華區']   
del count['龍華區']  
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')  
plt.axis('equal')  # 使餅圖爲正圓形  
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))  
plt.savefig('pie_chart.jpg')  
plt.show()  
​
# 繪製詞雲,將職位福利中的字符串彙總  
text = ''  
for line in df['職位福利']:  
   text += line  
# 使用jieba模塊將字符串分割爲單詞列表      
cut_text = ' '.join(jieba.cut(text))  
color_mask = imread('cloud.jpg')  #設置背景圖  
cloud = WordCloud(  
       font_path = 'yahei.ttf',   
       background_color = 'white',  
       mask = color_mask,  
       max_words = 1000,  
       max_font_size = 100          
       )  
​
word_cloud = cloud.generate(cut_text)  
# 保存詞雲圖片  
word_cloud.to_file('word_cloud.jpg')  
plt.imshow(word_cloud)  
plt.axis('off')  
plt.show()  
​
# 實證統計,將學歷不限的職位要求認定爲最低學歷:大專  
df['學歷要求'] = df['學歷要求'].replace('不限','大專')  
​
# 學歷分爲大專\本科\碩士,將它們設定爲虛擬變量  
dummy_edu = pd.get_dummies(df['學歷要求'],prefix = '學歷')  
# 構建迴歸數組  
df_with_dummy = pd.concat([df['月工資'],df['經驗'],dummy_edu],axis = 1)  
​
# 創建多元迴歸模型  
y = df_with_dummy['月工資']  
X = df_with_dummy[['經驗','學歷_大專','學歷_本科','學歷_碩士']]  
X=sm.add_constant(X)   
model = sm.OLS(y,X)  
results = model.fit()  
print('迴歸方程的參數:\n{}\n'.format(results.params))  
print('迴歸結果:\n{}'.format(results.summary()))
相關文章
相關標籤/搜索