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

一句話歸納本文javascript

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


引言java

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

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


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

在開始以前你可能須要大概瞭解下這三個庫:numpypandasmatplotlib, 數據分析必備三件套,考慮若是要把文檔吃透須要很多時間,還有文章篇幅 等緣由,這裏不慢慢去啃了,後續可能會寫單獨的教學章節,本節給出相關 的參考連接,有興趣可先行本身研究~git

:《Python for Data Analysis, 2nd Edition》github

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

numpy庫:

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

學習連接json

pandas庫:

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

學習連接

matplotlib庫

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

學習連接


2.數據爬取

打算分析一波深圳區的,打開首頁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, 前者是用做數據可視化展現的一個很強大的工具,然後者則是生成 地圖類的一個庫,還記得咱們採集到的經緯度麼?弄一個相似於熱力 圖的東東不是很酷麼?學習成本有點高,後面再了試試吧!


附:最終代碼(均可以在:github.com/coder-pig/R… 找到):

# 拉勾網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。

相關文章
相關標籤/搜索