小豬的Python學習之旅 —— 16.採集拉勾網數據分析Android就業行情

小豬的Python學習之旅 —— 16.再嘗Python數據分析:採集拉勾網數據分析Android就業行情

標籤:Python


一句話概括本文

爬取拉鉤Android職位相關數據,利用numpy,pandas和matplotlib對招人公司
情況和招聘要求進行數據分析。


引言

在寫完上一篇《淺嘗Python數據分析:分析2018政府工作報告中的高頻詞》,
一直都處於一種亢奮的狀態,滿腦子都想着數據分析,膜一下當然很開心,
更重要的是感受到了Python數據分析的好玩,迫不及待地想寫個新的東西玩玩,
這不,給我翻到一個好玩的東西:《Python拉鉤數據採集與可視化
就是採集拉鉤上關於Python崗位的相關信息,然後做數據分析,通過
圖表的形式把分析結果展示給別人看;在我潛水的很多個Android羣裏
普遍有這樣的反饋:儘管是現在是招聘的金三銀四,但是Android的工作
真不好找?原因是Android崗位稀缺?要求過高?薪資問題?又或者其他
因素,我決定通過Python來採集相關數據,利用numpy,pandas,matplotlib
數據分析基礎三件胡亂分析一波:

試圖從分析結果中獲取點什麼有用的信息,以便了解方便自己更好的瞭解
市場行情,不逼逼,開始本節內容~


1.知道下數據分析三件套

在開始之前你可能需要大概瞭解下這三個庫:numpypandasmatplotlib
數據分析必備三件套,考慮如果要把文檔吃透需要不少時間,還有文章篇幅
等原因,這裏不慢慢去啃了,後續可能會寫單獨的教學章節,本節給出相關
的參考鏈接,有興趣可先行自己研究~

:《Python for Data Analysis, 2nd Edition》

PDF下載:Python for Data Analysis, 2nd Edition.pdf
中文翻譯:利用Python進行數據分析·第2版

numpy庫:

科學計算基礎包,爲Python提供快速的數組處理能力,
作爲在算法和庫之間傳遞數據的容器。
NumPy是在在一個連續的內存塊中存儲數據,獨立於其他Python內置對象。
NumPy的C語言編寫的算法庫可以操作內存,而不必進行類型檢查或其它
前期工作。比起Python的內置序列,NumPy數組使用的內存更少。

學習鏈接

pandas庫:

提供了快速便捷處理結構化數據的大量數據結構和函數,有兩種數據
常見的數據結構,分別爲:Series (一維的標籤化數組對象)和
DataFrame (面向列的二維表結構)

學習鏈接

matplotlib庫

用於繪製圖表和其它二維數據可視化的Python庫

學習鏈接


2.數據爬取

打算分析一波深圳區的,打開首頁https://www.lagou.com/
進去後選擇深圳站,搜索欄輸入 android點進去後,發現有30頁:

隨手點下一頁,頁面沒有整個刷新,基本都是Ajax了,
F12開發者選項,打開抓包,Network選項卡clear一下,
選中XHR,點下一頁,喲,有兩個:

點擊preview看下具體的json內容:

不知道是什麼東西,隨手複製個451,然後切換到Element搜索,

喲,原來是公司id,拼接下可以獲得一個跳到公司詳細信息的url,
這裏暫時沒用,approve,譯作認證,就是認證公司id列表
跟着一個true,猜測是企業是否認證的標識,可能是頁面上
不顯示未認證企業或者顯示不一樣的UI吧,接着看下一個接口:

明顯就是我們想Get的數據,hrInfoMap字段是和HR相關的信息,沒用
跳過,最下面的result數組則是我們最關注的招聘信息了,點開一個
確認下:

知道了要爬哪裏的數據,接着就是到怎麼模擬請求了:

先是請求頭,因爲沒登錄就可以訪問了,Cookies就不用傳了,
其他的能帶上就帶上,多了也沒什麼,然後是鏈接後面附帶
的參數,固定的不用改:

  • city: 深圳
  • needAddtionalResult: false
  • isSchoolJob: 0

然後是Post提交的表單數據:

  • first: false
  • pn: 2
  • kd: android

pn是頁碼,其他的都不用動!都清楚了,接着就寫下代碼
模擬下了,流程都一清二楚了,不難寫出下面的代碼:

執行下,把結果貼到Json格式化工具裏:

可以,另外這裏還有個點可以get以下,就是總共有多少頁,總共有738條數據,
每頁顯示15個,738/15=49,測了下確實是最後一頁!

接着捋一捋想要採集的字段:

公司id招聘崗位id公司全名招聘職位名工作年限
學歷性質行業領域公司優勢, 薪資範圍公司規模
技能標籤融資狀態公司標籤所在區域公司經度
公司緯度

因爲我自己買的代理很多都是連接超時或者拒絕連接等各種問題,
所以直接用本機ip爬,隨機5-15s避免ip被封,趁着去吃飯的空檔,
讓他自己慢慢爬。一開始是打算寫正則來摳數據的,按照返回的
Json結構,我試着寫了這樣的正則:

運行後,看到爬取了4,5頁沒什麼問題,於是乎安心去吃飯了,後來
發現部分數據都亂套了,原來是有些頁面的Json字段順序不一樣,我服…

還是迴歸Json按照字段取穩妥,手動摳字段,拼成一個列表,
最後塞一個數組裏,最後通過pandas庫的to_csv()方法轉換
爲一個列表。


3.用pandas的to_csv方法把數據都塞Excel裏

代碼如下

執行完後會在當前工程下生成一個result.csv文件,打開檢查下數據是否
都正確,處理下髒數據,發現有五個數據位置是錯亂的:

依次查看錯亂的原因:

Android高級開發工程師&nbsp
最新iMAC&nbsp
雙休&nbsp
待遇豐厚&nbsp
Android系統&amp
移動開發經理 (Android &amp
移動App開發工程師(Android&amp

這裏簡直是巨坑,上面的&nbsp和&amp是Html裏的轉義字符,需要調用
html.unescape()方法轉義一波,對應相反的方法escape()
當爬取的內容是字符串的時候要小心這個坑!!!修改後的代碼:

總共採集到736條數據,不算多,但也可以開始做數據分析了。


4.數據分析前可能遇到的一些問題

1)matplotlib中文亂碼問題

主要使用條形圖,餅圖和詞雲來展示數據分析結果!
使用matplotlib進行圖標繪製,基本都會遇到的一個問題,中文亂碼
解決流程如下:

打開終端,cd到路徑下,然後準備一枚中文ttf,接着命令行sudo mv
把ttf文件拷貝到該路徑下:

接着雙擊安裝,安裝後修改配置文件,對如圖三處做相應修改:

修改完畢後,執行下述命令刪除一波緩存文件

接着再運行就可以了:

其他系統處理matplotlib中文亂碼問題自行參見:matplotlib圖例中文亂碼

2)matplotlib繪製顯示不全

如圖所示,在繪製的時候可能會出現顯示不全的情況:

順道介紹下按鈕,依次是:

重置回主視圖上一步視圖下一步視圖拖拽頁面
局部放大設置保存成圖片

然後的話,可以點下設置,會出現:

拖拉調整下,直到差不多能顯示完全

接着記錄下對應的參數,代碼中設置下:


5.開始數據分析

一.分析招聘公司的一些情況

行業領域

從詞雲可以看出,Android招聘大部分還是移動互聯網公司,接着依次是金融
硬件電子商務,電子商務?都是幹嘛的,利用pandas做下簡單篩選,

隨手搜了幾個百度下,這種從事電子商務大概是:
電商(有自己的購物APP,平臺),外包,支付,POS機等
接着是遊戲,數據服務,企業服務,o2o等等。

公司規模

融資狀態

PS:臥槽,15-50人和未融資公司的百分比竟然異常接近,應該大部分都是
小型的創業公司吧,基本都很坑…

所在區域

大部分招Android的公司還是集中在南山區,其次是福田區和寶安區,
小部分在龍華新區和龍崗區。

公司標籤

看下公司都打着怎麼樣的標籤招人

公司優勢

和上面一個樣,假如你公司要你寫招聘條件的時候,就不愁寫什麼啦~

二.分析對招聘者的一些要求

工作年限

招最多的是3-5年工作經驗,其次是1-3年,再接着是5-10年

學歷要求

以前以爲學歷不重要,然而圖中本科要求佔比71.8%,沒有本科學歷意味着:
可能失去七成的機會,哭哭/(ㄒoㄒ)/~~,我這種渣渣還是繼續當鹹魚吧…

薪資情況

拉鉤標的薪酬都是有個範圍的,其實一般最小值就是你實際進公司後的
薪資,直接以最小值作爲參考,圖中明顯的三個小高峯,10k,15k,8k。
猜測對應:
10k - 3年經驗左右的一般的中初級工程師
15k - 3-5年經驗的技術較好的中高級工程師
8k - 1到3年內的初中級工程師
當然土豪公司不在範圍內,看完自己找工作的時候開口要多少錢,
心裏應該有點B數啦。(哭哭,又拖後腿了/(ㄒoㄒ)/~~)

技能標籤

Java, iOS,架構,C++,系統開發,中級,高級,資深,視頻,信息安全…

要求Java我能理解,這要求iOS是什麼鬼?現在找個Android崗都要會iOS了?
後來才發現並沒那麼誇張,只是招聘職位那裏Android/iOS,所以纔有iOS
的標籤,還有些瞎幾把填的標籤,進去招聘要求裏一個iOS的字眼也沒有。

到此就分析完啦~


6.小結

本節依次抓取了一波拉勾網和Android職位相關的數據,利用這些數據對
公司情況和招聘要求進行了分析,通過圖表以及詞雲的形式,儘管沒有
得出非常有用的信息,不過應該也get了不少東西,上上週週一就立flag
出的,結果因爲項目發新版本,自己感冒涼了幾天,一直拖到現在…
因爲篇幅和時間關係,有兩個遺憾:Jupyter Notebookgeopandas
前者是用作數據可視化展示的一個很強大的工具,而後者則是生成
地圖類的一個庫,還記得我們採集到的經緯度麼?弄一個類似於熱力
圖的東東不是很酷麼?學習成本有點高,後面再了試試吧!


附:最終代碼(都可以在:https://github.com/coder-pig/ReptileSomething 找到):

# 拉勾網Android招聘數據分析
import urllib.parse
import requests
import xlwt
import xlrd
import tools as t
import pandas as pd
import geopandas as gp
import re
import random
import time
import html
import matplotlib.pyplot as plt
import numpy as np
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
from collections import Counter
from scipy.misc import imread
import config as c
from shapely.geometry import Point, Polygon

max_page = 1
result_save_file = c.outputs_logs_path + 'result.csv'
pic_save_path = c.outputs_pictures_path + 'LaGou/'
default_font = c.res_documents + 'wryh.ttf'  # 生成詞雲用的默認字體
default_mask = c.res_pictures + 'default_mask.jpg'  # 默認遮罩圖片

# Ajax加載url
ajax_url = "https://www.lagou.com/jobs/positionAjax.json?"

# url拼接參數
request_params = {'px': 'default', 'city': '深圳', 'needAddtionalResult': 'false', 'isSchoolJob': '0'}

# post提交參數
form_data = {'first': 'false', 'pn': '1', 'kd': 'android'}

# 獲得頁數的正則
page_pattern = re.compile('"totalCount":(\d*),', re.S)

# csv表頭
csv_headers = [
    '公司id', '職位名稱', '工作年限', '學歷', '職位性質', '薪資',
    '融資狀態', '行業領域', '招聘崗位id', '公司優勢', '公司規模',
    '公司標籤', '所在區域', '技能標籤', '公司經度', '公司緯度', '公司全名'
]

# 模擬請求頭
ajax_headers = {
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'Connection': 'keep-alive',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Host': 'www.lagou.com',
    'Origin': 'https://www.lagou.com',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 '
                  'Safari/537.36',
    'X-Anit-Forge-Code': '0',
    'X-Anit-Forge-Token': 'None',
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': 'https://www.lagou.com/jobs/list_android?labelWords=&fromSearch=true&suginput='
}


# 獲取每頁招聘信息
def fetch_data(page):
    fetch_url = ajax_url + urllib.parse.urlencode(request_params)
    global max_page
    while True:
        try:
            form_data['pn'] = page
            print("抓取第:" + str(page) + "頁!")
            resp = requests.post(url=fetch_url, data=form_data, headers=ajax_headers)
            if resp.status_code == 200:
                if page == 1:
                    max_page = int(int(page_pattern.search(resp.text).group(1)) / 15)
                    print("總共有:" + str(max_page) + "頁")
                data_json = resp.json()['content']['positionResult']['result']
                data_list = []
                for data in data_json:
                    data_list.append((data['companyId'],
                                      html.unescape(data['positionName']),
                                      data['workYear'],
                                      data['education'],
                                      data['jobNature'],
                                      data['salary'],
                                      data['financeStage'],
                                      data['industryField'],
                                      data['positionId'],
                                      html.unescape(data['positionAdvantage']),
                                      data['companySize'],
                                      data['companyLabelList'],
                                      data['district'],
                                      html.unescape(data['positionLables']),
                                      data['longitude'],
                                      data['latitude'],
                                      html.unescape(data['companyFullName'])))
                    result = pd.DataFrame(data_list)
                if page == 1:
                    result.to_csv(result_save_file, header=csv_headers, index=False, mode='a+')
                else:
                    result.to_csv(result_save_file, header=False, index=False, mode='a+')
                return None
        except Exception as e:
            print(e)


# 生成詞雲文件
def make_wc(content, file_name, mask_pic=default_mask, font=default_font):
    bg_pic = imread(mask_pic)
    pic_colors = ImageColorGenerator(bg_pic)
    wc = WordCloud(font_path=font, background_color='white', margin=2, max_font_size=250,
                   width=2000, height=2000,
                   min_font_size=30, max_words=1000)
    wc.generate_from_frequencies(content)
    wc.to_file(file_name)


# 數據分析方法(生成相關文件)
def data_analysis(data):
    # 1.分析招聘公司的相關信息
    # 行業領域
    industry_field_list = []
    for industry_field in data['行業領域']:
        for field in industry_field.strip().replace(" ", ",").replace("、", ",").split(','):
            industry_field_list.append(field)
    counter = dict(Counter(industry_field_list))
    counter.pop('')
    make_wc(counter, pic_save_path + "wc_1.jpg")

    # 公司規模
    plt.figure(1)
    data['公司規模'].value_counts().plot(kind='pie', autopct='%1.1f%%', explode=np.linspace(0, 0.5, 6))
    plt.subplots_adjust(left=0.22, right=0.74, wspace=0.20, hspace=0.20,
                        bottom=0.17, top=0.84)
    plt.savefig(pic_save_path + 'result_1.jpg')
    plt.close(1)
    # 融資狀態
    plt.figure(2)
    data['融資狀態'].value_counts().plot(kind='pie', autopct='%1.1f%%')
    plt.subplots_adjust(left=0.22, right=0.74, wspace=0.20, hspace=0.20,
                        bottom=0.17, top=0.84)
    plt.savefig(pic_save_path + 'result_2.jpg')
    plt.close(2)
    # 所在區域
    plt.figure(3)
    data['所在區域'].value_counts().plot(kind='pie', autopct='%1.1f%%', explode=[0, 0, 0, 0, 0, 0, 0, 1, 1.5])
    plt.subplots_adjust(left=0.31, right=0.74, wspace=0.20, hspace=0.20,
                        bottom=0.26, top=0.84)
    plt.savefig(pic_save_path + 'result_3.jpg')
    plt.close(3)
    # 公司標籤
    tags_list = []
    for tags in data['公司標籤']:
        for tag in tags.strip().replace("[", "").replace("]", "").replace("'", "").split(','):
            tags_list.append(tag)
    counter = dict(Counter(tags_list))
    counter.pop('')
    make_wc(counter, pic_save_path + "wc_2.jpg")
    # 公司優勢
    advantage_list = []
    for advantage_field in data['公司優勢']:
        for field in advantage_field.strip().replace(" ", ",").replace("、", ",").replace(",", ",").replace("+", ",") \
                .split(','):
            industry_field_list.append(field)
    counter = dict(Counter(industry_field_list))
    counter.pop('')
    counter.pop('移動互聯網')
    make_wc(counter, pic_save_path + "wc_3.jpg")

    # 2.分析招聘需求
    # 工作年限要求
    # 橫向條形圖
    plt.figure(4)
    data['工作年限'].value_counts().plot(kind='barh', rot=0)
    plt.title("工作經驗直方圖")
    plt.xlabel("年限/年")
    plt.ylabel("公司/個")
    plt.savefig(pic_save_path + 'result_4.jpg')
    plt.close(4)
    # 餅圖
    plt.figure(5)
    data['工作年限'].value_counts().plot(kind='pie', autopct='%1.1f%%', explode=np.linspace(0, 0.75, 6))
    plt.title("工作經驗餅圖")
    plt.subplots_adjust(left=0.22, right=0.74, wspace=0.20, hspace=0.20,
                        bottom=0.17, top=0.84)
    plt.savefig(pic_save_path + 'result_5.jpg')
    plt.close(5)
    # 學歷要求
    plt.figure(6)
    data['學歷'].value_counts().plot(kind='pie', autopct='%1.1f%%', explode=(0, 0.1, 0.2))
    plt.title("學歷餅圖")
    plt.subplots_adjust(left=0.22, right=0.74, wspace=0.20, hspace=0.20,
                        bottom=0.17, top=0.84)
    plt.savefig(pic_save_path + 'result_6.jpg')
    plt.close(6)

    # 薪資(先去掉後部分的最大工資,過濾掉kK以上詞彙,獲取索引按照整數生序排列)
    plt.figure(7)
    salary = data['薪資'].str.split('-').str.get(0).str.replace('k|K|以上', "").value_counts()
    salary_index = list(salary.index)
    salary_index.sort(key=lambda x: int(x))
    final_salary = salary.reindex(salary_index)
    plt.title("薪資條形圖")
    final_salary.plot(kind='bar', rot=0)
    plt.xlabel("薪資/K")
    plt.ylabel("公司/個")
    plt.savefig(pic_save_path + 'result_7.jpg')
    plt.close(7)

    # 技能標籤
    skill_list = []
    for skills in data['技能標籤']:
        for skill in skills.strip().replace("[", "").replace("]", "").replace("'", "").split(','):
            skill_list.append(skill)
    counter = dict(Counter(skill_list))
    counter.pop('')
    counter.pop('Android')
    make_wc(counter, pic_save_path + "wc_4.jpg")


# 處理數據
if __name__ == '__main__':
    t.is_dir_existed(pic_save_path)
    if not t.is_dir_existed(result_save_file, mkdir=False):
        fetch_data(1)
        for cur_page in range(2, max_page + 1):
            # 隨緣休息5-15s
            time.sleep(random.randint(5, 15))
            fetch_data(cur_page)
    else:
        raw_data = pd.read_csv(result_save_file)
        # data_analysis(raw_data)
        # 篩選電子商務公司
        dzsw_result = raw_data.loc[raw_data["行業領域"].str.find("電子商務") != -1, ["行業領域", "公司全名"]]
        dzsw_result.to_csv(c.outputs_logs_path + "dzsw.csv", header=False, index=False, mode='a+')
        # 篩選人15-50人的公司
        p_num_result = raw_data.loc[raw_data["所在區域"] == "龍華新區", ["所在區域", "公司全名"]]
        p_num_result.to_csv(c.outputs_logs_path + "lhxq.csv", header=False, index=False, mode='a+')

來啊,Py交易啊

想加羣一起學習Py的可以加下,智障機器人小Pig,驗證信息裏包含:
PythonpythonpyPy加羣交易屁眼 中的一個關鍵詞即可通過;

驗證通過後回覆 加羣 即可獲得加羣鏈接(不要把機器人玩壞了!!!)~~~
歡迎各種像我一樣的Py初學者,Py大神加入,一起愉快地交流學♂習,van♂轉py。


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