摘要:成功爬取了拉鉤網上多個招聘崗位的具體信息后,數據可視化并得出分析結果如下從整體看,北上廣深杭這五個城市前端工程師招聘崗位,北京是遙遙領先,是深圳的兩倍,是廣州的三倍,其次到上海,深圳,杭州,廣州居末。
前前言
本文首發于 github blog
不想看爬蟲過程只想看職位錢途數據分析請看這里:
前端招聘崗位分析
C++招聘崗位分析
JAVA招聘崗位分析
PHP招聘崗位分析
Python招聘崗位分析
想看源碼或想自己爬一個請看這里:本文github源碼
前言早在一年前大學校招期間,為了充實下簡歷,就寫了個node爬蟲,可惜當時能力有限,工程存在一定的局限性,不好意思拿出來裝逼分享。
一年過去了,現在能力依然有限,但是臉皮卻練厚了,于是就有了這篇文章。
題綱關于爬蟲,主流技術是用python,然而隨著node的出現,對于對python了解有限的前端同學,用node來實現一個爬蟲也不失為一個不錯的選擇。
當然無論是python爬蟲還是node爬蟲或者其他品種的爬蟲,其實除了語言特性之外,其思路基本大同小異。下面我就為大家詳細介紹下node爬蟲的具體思路與實現,內容大概如下:
爬前準備
選擇目標
分析可收集數據與目標可爬取入口
爬蟲
爬取JSON數據
爬取HTML文檔,提取有用信息
Mongodb 數據存儲
并發控制
動態IP代理(防止IP被禁)
數據可視化展示
爬前準備 選擇目標既然要寫爬蟲,當然要爬一些利益相關的數據比較好玩啦。爬取招聘網站的招聘信息,來看看互聯網圈子里各個工種的目前薪酬狀況及其發展前景,想來是不錯的選擇。
經我夜觀天下,掐指一算,就選拉勾網吧。
分析可收集數據一個職位招聘信息,一般來說,我們關注的重點信息會是:
薪酬(毫無疑問,重中之重)
工作城市
學歷要求
工作年限要求
雇主公司
公司領域
公司規模
帶著想要收集的信息,首先,進入拉勾官網,搜索web前端崗位,能看到
很好,我們想要的信息基本都有了。
分析目標可爬取入口 PC端入口F12 分析請求資源,可得https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false&isSchoolJob=0
post 請求體
{ first:false, pn:1, kd:`web前端` }
響應JSON數據
完美!!! 數據格式都已經幫我們整理好了,直接爬就行了。
但,完美的數據總不會這么輕易讓你得到,經我用 node 和 python,還有postman 攜帶瀏覽器全部header信息一一測試,均發現:
好吧,此路不通。(此接口反爬蟲機制不明,有研究的大神請留言=_=)
所謂條條大路通羅馬,此路不通,咱繞路走。
移動端入口經過一番探索,發現 拉勾移動端站點 空門大開!
提示: 一般有點技術含量的網站都可能會存在不同強度的反爬蟲機制,而一般其移動端站點的反爬蟲機制相對于PC站點較弱,是一個不錯的著手點。再不行的話,還可以去其app端抓包分析是否存在想要的請求哦。
GET請求: https://m.lagou.com/search.js...
響應信息:
很好,雖然數據信息有點少,但是總算是一個能爬的接口了。
爬蟲好了,分析也分析完了,現在正式設計爬蟲程序。
JSON數據爬取
首先,把請求的路徑與參數多帶帶抽離。
let spider = { requestUrl : "http://m.lagou.com/search.json", query: { city: "", pageNum: "", job: "", }, ... }
發出請求,此處的服務端構造請求使用 superagent,當然,用 request 等類似的包也可以,并無限定。
let spider = { .... /** * 發起單個請求 * @return {> | >} 請求成功resolve原始數據,否則reject **/ request() { return new Promise((resolve,reject)=>{ superagent .get(this.requestUrl) .query({ city: this.query.city, pageNo: this.query.pageNum, positionName: this.query.job }).end((err, res)=>{ let dataList = []; if (err || !res || !res.ok) { console.error(err); reject("request failed!") } else { dataList = res.body.content.data.page.result if (dataList.length === 0) { // 當請求結果數組長度為0,即認為已經到末頁,結束爬蟲 reject("finish"); } else { resolve(dataList) } } }) }) },
處理數據
let spider = { .... /** * 處理爬取到的原始數據,提取出所需的數據 * @param {} - companyList : 原始數據 * @return { >} resolve處理過的數據 **/ handleCallbackData(companyList) { //處理數據 let arr = companyList.map((item) => { let salary = item.salary.split("-"); //工資兩種情況:”10k以上“ or "10k-15k", 平均工資取中位數 aveSalary = salary.length == 1 ? parseInt(salary[0])*1000 : (parseInt(salary[0]) + parseInt( salary[1] ) )*500; //過濾出所需數據 return { companyFullName: item.companyFullName, positionId : item.positionId , salary:aveSalary , city:item.city , field: "", companySize:"", workYear:"" , qualification: "", } }); return Promise.resolve(arr) }
保存數據,此處數據庫使用mongodb,ORM使用 moogoose。
save2db(jobList) { return new Promise((resolve, reject)=>{ Job.create(jobList,function (err,product) { if (err) { console.error(err.errmsg) err.code == 11000 && resolve("丟棄重復數據") reject(err); } else { resolve("save data to database successfully") } }) }) },HTML 數據解析爬取
從上述的json數據其實我們可以看到,JSON返回的信息十分有限,那么我們需要爬取更多的信息,就需要在招聘詳情頁解析 html 后提取出所需的信息
隨便打開一個移動端的招聘詳情頁https://m.lagou.com/jobs/3638173.html,目測出url結構很簡單,就是jobs/{{positionId}}.html
從詳情頁中可以找出 JSON 數據中缺少的數據項:工作年限要求,學歷要求,雇主公司領域,雇主公司融資情況,雇主公司規模大小。
爬取方法和上述爬取 JSON 數據相差無幾,主要差別就是數據解析部分,這里需要用到cherrio來解析 爬取到的HTML,從而更簡單地提取必要信息。
handleCallbackData({res, jobId}) { var $ = cheerio.load(res.text); let workYear = $("#content > div.detail > div.items > span.item.workyear > span").text(), qualification = $("#content > div.detail > div.items > span.item.education").text().trim(), field = $("#content > div.company.activeable > div > div > p").text().trim().split(/s*/s*/)[0] companySize = $("#content > div.company.activeable > div > div > p").text().trim().split(/s*/s*/)[2]; /* 如果這四項數據都沒有提取到,很有可能是被拉勾的反爬蟲機制攔截了 */ if ( !(workYear || qualification || field || companySize) ) { console.log(res.text) return Promise.reject({code:-1, msg:"wrong response!", jobId}); } return { id: jobId, jobInfo: { workYear, qualification, field, // financeStage, companySize, } } },并發控制
做過爬蟲的都知道,爬蟲的請求并發量是必須要做的,為什么要控制并發?
控制其爬取頻率,以免沒爬幾個就網站被封IP了。
控制爬蟲應用運行內存,不控制并發的話一下子處理N個請求,內存分分鐘爆炸。
實現并發控制可以使用npm包 async.mapLimit,這里為了自由度更大我使用了自己實現的 15 行代碼實現并發控制。
具體代碼如下:
let ids = [2213545,5332233, ...], // 招聘崗位詳情id列表 limit = 10, // 并發數 runningRequestNum = 0 , // 當前并發數 count = 0; // 累計爬取數據項計數 mapLimit(ids, limit, async (jobId)=>{ let requestUrl = `http://m.lagou.com/jobs/${jobId}.html?source=home_hot&i=home_hot-6` ; let delay = parseInt(Math.random() * 2000); let currentIndex = count++; runningRequestNum++ await sleep( delay ); // 避免爬太快被封ip,休眠一兩秒 let result = await spiderHTML.run({ requestUrl, jobId, proxyIp }) console.log(`當前并發數`, runningRequestNum) runningRequestNum-- return result; }).then(mapResult => { // 并發控制下將 ids 全部迭代完畢 // do something })
然而,即便嚴格控制了請求頻率,我們還是不可避免地中招了。
對于反爬蟲措施比較暴躁的網站來說,一個IP爬取太過頻繁,被識別成機器爬蟲幾乎是不可避免的。
一般來講,我們最簡單直接的方法就是:換IP。這個IP訪問頻率太高了被反爬攔截到,換個IP就行了嘛。
動態IP代理單個IP爬蟲對于反爬較為嚴厲的網站是走不通的。那么我們需要用到動態IP池,每次爬取時從IP池中拉取一個IP出來爬數據。
道理很簡單,
1秒內1個IP訪問了100個頁面,即便是單身20多年的手速也無法企及。只能是機器爬蟲無疑。
但1秒內100個IP訪問100個頁面,平均每個IP一秒內訪問了1個頁面,那基本不會被反爬干掉
怎么搭建動態IP池?
首先我們得有一個IP源,動態IP池的補充都從這里拉取,這個網上搜一下"免費代理IP"就有很多出來,選其中一個,收費的IP源比較穩定可靠,免費的就一分錢一分貨了。
其次,每次從IP源中拉取的IP都是無法確認其是否可用的,我們必須篩選一遍,提取出可用的IP。(PS: 此處和步驟4目的一直,如果IP源較為可靠,可以省略)
設計從IP池中拉取單個IP的策略,使得每個IP使用頻率均勻,盡量避免單個IP使用頻率過高而失效。
移除失效IP。盡管設計了拉取策略,但依舊不可避免某些IP失效,此時需要將其移出IP池廢棄。
動態IP池工作流程:
st=>start: 提取一個可用IP e=>end: 根據策略返回一個可用IP isEnought=>condition: 池中IP數量是否足夠 fetch=>operation: 從IP源拉取IP valid=>operation: 篩選出有效IP并存入IP池 st->isEnought(yes)->e isEnought(no,right)->fetch(right)->valid(right)->isEnought
具體實現代碼其實和上面的爬蟲差不多,無非就是爬崗位變成了爬IP而已,具體實現源碼在這,就不在這寫了。
數據可視化分析我們最終折騰爬蟲,無非就是想要看爬到的數據到底說明了什么。
成功爬取了拉鉤網上多個招聘崗位的具體信息后,數據可視化并得出分析結果如下:
從整體看,北上廣深杭這五個城市前端工程師招聘崗位,北京是遙遙領先,是深圳的兩倍,是廣州的三倍,其次到上海,深圳,杭州,廣州居末。
從需求量大概可以看出,整體互聯網產業發達程度是 北 > 上 > 深 > 杭 > 廣
由平均工資曲線圖可以看到,每隔2K算一檔的話,北京一檔,上海一檔,杭州深圳一檔,空一檔,廣州吊車尾,杭州竟然比深圳高了300,這就代表著深圳雖然招聘需求比杭州大,但兩者薪酬待遇其實差不多。
從不同薪酬的招聘數量也能看出一些很大的區別,招聘提供薪資水平中,普遍數量最多的是10k-20k這個水平,但,北京牛逼,招聘崗位60%以上都是20K以上的。我們具體來看看,各個城市對高端人才(提供薪酬20k以上)的招聘比例,那就可以看出明顯區別了:
北京:招聘的薪資水平是"20k以上",大概是招聘總數的59.7%
上海:招聘的薪資水平是"20k以上",大概是招聘總數的41.3%
深圳:招聘的薪資水平是"20k以上",大概是招聘總數的29.2%
杭州:招聘的薪資水平是"20k以上",大概是招聘總數的30.4%,和深圳相差不大
廣州:招聘的薪資水平是"20k以上",大概是招聘總數的……10.4%。
基本可以看到一個明顯的趨勢,公司規模越大,能提供的薪酬越高,不差錢。
另外,從不同規模的公司的前端招聘數量來看,北京又一枝獨秀,大公司招聘需求很高。
但從全國來看,不同規模的公司(除了15人以下的)招聘數量基本在同一水平,基本說明:大公司少,但是每個公司招聘的人多;小公司多,但是每個公司招聘的人少。好像這是句廢話。
從圖上看,工作經歷在1-5年的現在需求最旺盛,并且理所當然地,工作資歷越高,薪資越高。
其中3-5年的最吃香,廣州有點奇怪,1-3年的最吃香?綜合上面的多項數據,感覺像是1-3年工資比3-5年低所以廣州互聯網公司多招1-3年
當然,這里存在這一個幸存者偏差,拉勾上大部分的都是社招性質的招聘,而應屆生和1年經驗的大部分都跑校招去了吧,所以數量低也不出奇。
移動互聯網占據了大半壁江山,剩下之中,金融,電子商務,企業服務,數據服務在同一層次。另外,物聯網,智能硬件各有一招聘崗位,薪酬都是5K...嗯雖說node現在也可以做物聯網了(還別說,我還真的用node搞過硬件串口通信Orz),但是終究不是主流技術,數據展示表明,前端基本與硬件絕緣。
薪酬待遇倒是都在同一水平上,“大數據”工資倒是一枝獨秀,但是數據量太少,參考價值不大。
總結:北京錢多機會多當之無愧第一檔;上海稍遜一籌;杭州深圳又低一籌;廣州真的是差了兩個身位。 而對于前端來說,北京 移動互聯網 大公司,錢多!坑多!速來!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93896.html
摘要:實際上,前程無憂招聘網站上與數據有關的只有幾百頁,而我們爬取了頁的所有數據,因此在后面進行數據處理時需要把無關的數據剔除掉。 目錄 一、項目背景 二、數據爬取 1、相關庫的導入與說明 2、獲取二級頁面鏈接 1)分析一級頁面url特征 2)構建一級url庫 3)爬取所有二級url鏈接 3、獲取...
摘要:因為本人在成都從事前端,所以這次爬取的關鍵詞既是成都,前端。僅僅有這個是不夠的,因為貌似拉勾網有反爬蟲,沒有好像得不到數據這個還待論證,至少我這邊是。 前言 showImg(https://segmentfault.com/img/bV1g4S?w=700&h=490); 今天是2018的第一天,首先祝各位小伙伴元旦快樂!又到了新的一年,雖然離春節還有一段時間,但是程序狗打工不易啊,不...
摘要:因為本人在成都從事前端,所以這次爬取的關鍵詞既是成都,前端。僅僅有這個是不夠的,因為貌似拉勾網有反爬蟲,沒有好像得不到數據這個還待論證,至少我這邊是。 前言 showImg(https://segmentfault.com/img/bV1g4S?w=700&h=490); 今天是2018的第一天,首先祝各位小伙伴元旦快樂!又到了新的一年,雖然離春節還有一段時間,但是程序狗打工不易啊,不...
摘要:最近在研究區塊鏈,閑來無事抓取了拉勾網上條區塊鏈相關的招聘信息。拉勾網的反爬蟲做的還是比較好的,畢竟自己也知道這種做招聘信息聚合的網站很容易被爬,而且比起妹子圖這種網站,開發的技術水平應該高不少。 最近在研究區塊鏈,閑來無事抓取了拉勾網上450條區塊鏈相關的招聘信息。過程及結果如下。 拉勾網爬取 首先是從拉勾網爬取數據,用的requests庫。拉勾網的反爬蟲做的還是比較好的,畢竟自己也...
摘要:智聯其實一共寫了兩次,有興趣的可以在源碼看看,第一版的是回調版,只能一次一頁的爬取。 寫在前面的話, .......還是不寫了,直接上效果圖。附上源碼地址 github.lonhon showImg(https://segmentfault.com/img/bVUM3F?w=714&h=543);showImg(https://segmentfault.com/img/bVUM...
閱讀 5265·2021-09-22 15:59
閱讀 1856·2021-08-23 09:42
閱讀 2561·2019-08-29 18:42
閱讀 3444·2019-08-29 10:55
閱讀 2058·2019-08-27 10:57
閱讀 1759·2019-08-26 18:27
閱讀 2722·2019-08-23 18:26
閱讀 2912·2019-08-23 14:40