1. 程式人生 > >如何用爬蟲抓取招聘網站的職位並分析

如何用爬蟲抓取招聘網站的職位並分析

最近有不少程式設計師又開始找工作了,為了瞭解目前技術類各職位的數量、薪資、招聘公司、崗位職責及要求,我爬取了拉勾網北上廣深4個城市的招聘資料,共3w條。職位包括:人工智慧(AI)、大資料、資料分析、後端(Java、C|C++、PHP、Python)、前端、Android、iOS、嵌入式和測試。下面我將分兩部分進行介紹,第一部分是資料抓取;第二部分是資料分析。如需原始碼在公眾號(見文末)回覆關鍵字 職位 即可。如遇到圖片打不開的問題,可訪問:https://juejin.im/post/5e83edfbf265da47d537ba74

資料抓取

這裡我並不是通過傳統的抓網頁,解析HTML程式碼的方式爬取資料,而是用 Charles 軟體抓取拉鉤APP請求資料的介面實現。

大概的流程是啟動Charles -> 手機連線Charles代理(二者需處於同一個區域網)-> 開啟APP請求資料->觀察Charles截的包,從中找到我們想要的介面

首先,找到搜尋職位的介面

/v1/entry/positionsearch/searchPosition

這是一個 POST 請求,我們還要找到請求的 header 和 body,最關鍵的 header 和 body 如下

header:

'X-L-REQ-HEADER': '{"deviceType":150,"userType":0,"lgId":"11835BCC-8815-456A-A094-64FB2B9323EF_1585362240","reqVersion":73600,"appVersion":"7.36.0","userToken":"xxx"}'
'content-type': "application/json"

其中,userToken欄位每個不一樣,需要自己抓包確定

body

{"tagType": "", "isAd": "1", "showId": "", "district": "", "keywordSource": 0, "keyword": "資料開發",
                "salaryUpper": 0, "hiTag": "", "longitudeAndLatitude": "-1.000000,-1.000000", "pageNo": 1, "sort": 0,
                "pageSize": 15, "refreshHiTagList": True, "lastShowCompanyId": 0, "nearByKilometers": "", "city": "北京",
                "businessZone": "", "shieldDeliveyCompany": False, "salaryLower": 0, "subwayLineName": "",
                "subwayStation": ""}  

其中,我們只需要關注 keyword,pageNo,pageSize欄位,分別代表搜尋什麼職位,搜尋第幾頁,每頁搜多少條。

有了這個資訊我們就可以通過程式來請求不同的職位資料,同時為了獲取職位更詳細的資訊我們還可以查詢獲取職位詳情頁的介面,方式與此類似,這裡就不再贅述了。請求職位的程式碼如下

def get_data(self):
        for city in self.cities_conf:
            for position in self.positions_conf:

                self.position_search_body['keyword'] = position
                self.position_search_body['city'] = city

                pageNo = 1
                has_more = 1
                while has_more:
                    try:
                        self.position_search_body['pageNo'] = pageNo
                        url = 'https://gate.lagou.com/v1/entry/positionsearch/searchPosition'
                        res = requests.post(url, data=json.dumps(self.position_search_body), headers=self.headers)
                        print('成功爬取%s市-%s職位的第%d頁資料!' % (city, position, pageNo))
                        item = {'city': city, 'pType': position}
                        print(res.json())
                        positionCardVos = res.json()['content']['positionCardVos']
                        self._parse_record(positionCardVos, item)

                        pageNo += 1
                        if positionCardVos is None or len(positionCardVos) < 15:
                            has_more = 0

                        time.sleep(random.random() * 5)
                    except Exception as e:
                        msg = '連結訪問不成功,正在重試!Exception: %s' % e
                        print(msg)
                        time.sleep((1 + random.random()) * 10)

 

變數 position 代表不同的職位,這裡請求的時候會加隨機停留時間,目的為了防止請求過於平凡。我們抓去別人的資料應該注意這一點,不能惡意爬別人的資料。應該模擬得更像普通人一樣去請求資料,如果請求過於頻繁導致別人服務出現問題那真實罪大惡極。

_parse_record 方法是解析請求的資料,並存入mongo。首先解析資料沒什麼好說的,就是解析json而已。簡單說下為什麼存入mongo,第一,解析的json資料,mongo儲存就是用json格式,讀取和寫入非常方便;第二,mongo不用提前設計表Schema,對我們這種臨時性和不確定性的分析帶來方便;第三,mongo可以儲存海量的資料;第四,mongo會快取熱點資料,我們在後續分析時候讀取會非常快。

_parse_record 方法程式碼如下,為了避免囉嗦,我只保留部分欄位的解析,其他的程式碼可以下載詳細程式碼來看

def _parse_record(self, data, item):
        if data:
            for position in data:
                item['pId'] = position.get('positionId')
                item['_id'] = '%s_%s_%d' % (item['city'], item['pType'], item['pId'])
          # ... 省略

                try:
                    position_detail_res = requests.get(self.position_detail_url % item['pId']
                                                       , timeout=20, headers=self.headers)  # 請求詳情頁的資料
                    position_content = position_detail_res.json()['content']
                    item['pAdvantage'] = position_content.get('positionAdvantage')
            # ...省略
            
            time.sleep(random.random() * 2)
          except Exception as e: 
            msg = '抓去職位%d詳情頁失敗, Exception: %s' % (item['pId'], e) 
            print(msg) 
            self.db['positions'].update_one({'_id': item['_id']}, {'$set': item}, upsert=True) 
            msg = '成功儲存資料:{}!'.format(item) 
            print(msg)

 

可以看到方法中還請求了職位詳情資料來豐富每一條資料的維度。  

資料分析

抓取資料後下面就是分析了,主要用pandas進行統計和畫圖。由於程式碼是用jupyter寫的,這裡不方便貼,所以我直接貼結論,感興趣的朋友可以自行檢視詳細程式碼。

1、哪個城市目前招聘的崗位多

可以看到,目前北京招聘的崗位最多,其次是上海和深圳,廣州是最少的。

2、每個城市各崗位的需求量

北上廣深4個城市目前招聘較多的崗位主要是後端-Java、前端、AI和測試。

3、各崗位的平均薪資情況

AI崗位的薪資最高,平均每個月30k以上;其次是大資料崗位,平均每個月26k左右,iOS的平均薪資比Android稍微高一些。另外,目前的前端崗位平均薪資偏低。

4、幾年工作經驗比較吃香

 

以北京招聘資料為例,目前招聘的各崗位都是以3-5年工作經驗為主,1-3年經驗的需求量不大。所以,這裡也要提醒職場新人,不要輕易跳槽。

其他城市的分佈情況與北京類似,這裡就不貼圖了。

5、什麼學歷比較吃香

 

以北京為例,目前招聘的崗位除了AI需要不少的碩士甚至博士外,其他崗位以本科學歷為主。

其他城市分佈與北京類似。

6、什麼規模公司對崗位需求大

                                                                             北京

 

                                                                               廣州

可以看到,北京招聘的企業主要是2000人以上規模的大公司,上海和深圳的分佈與北京類似。而廣州在AI、前端和後端-Java幾個崗位的招聘主要以50-1000人的中等規模公司為主。

7、HR什麼時間段更活躍

以北京為例,各崗位的HR大部分在下午活躍,所以大家可以將簡歷的投遞時間選在下午。

其他城市分佈與北京類似。

8、崗位的職責和要求

限於篇幅,我只跑了AI、後端-Java和前端這3個崗位的資料,以詞雲的形式展現

希望這次分析能對你有用,歡迎公眾號「渡碼」,回覆關鍵字“職位”即可獲取本次分析的原始碼。