今天是2018的第一天,首先祝各位小夥伴元旦快樂!
又到了新的一年,雖然離春節還有一段時間,可是程序狗打工不易啊,不關注薪資怎麼行。今天要作的就是用圖表統計一下如今各公司的薪資情況(雖然不少公司不能按照招聘上他們給的薪資來給)。javascript
本次使用scrapy來作數據爬取,這是一個python的框架。由於本人在成都從事web前端,因此此次爬取的關鍵詞既是:成都,web前端。html
scrapy startproject lagou
首先經過運行命令,獲得一個爬蟲項目的基礎結構。前端
接着按照scrapy的中文教程,經過在java
start_urls = [ "https://www.lagou.com/jobs/list_web%E5%89%8D%E7%AB%AF?labelWords=sug&fromSearch=true&suginput=web" ]
spider中的start_urls配置好,應該就能把拉勾網頁面拉取下來,而後再分析dom,提取字符串就能夠了,無奈這種方法並不行。python
起初也不知道,就用xpath一直找,後來發現找不到會報錯,這些各類錯誤對於我這個爬蟲萌新仍是懵逼的。仔細查看他的network發現,他的招聘信息都是在另外的ajax請求當中,而且仍是整理好的。git
由於本人工做1年多,因此主要關注點是3年如下及3-5年,就提早選好了,城市和工做年限。該請求的傳參是formdata,其中first是首頁(其實寫代碼的時候並無注意這個參數,因此一直傳的是true,貌似也沒什麼影響),pn是當前頁數,kd是關鍵詞。github
因而乎就去文檔查閱了一下,如何在scrapy中循環發送formdata請求。最終獲得這樣一段能夠執行的代碼。web
def start_requests(self): url = "https://www.lagou.com/jobs/positionAjax.json?gj=3%E5%B9%B4%E5%8F%8A%E4%BB%A5%E4%B8%8B%2C3-5%E5%B9%B4&xl=%E6%9C%AC%E7%A7%91&px=default&city=%E6%88%90%E9%83%BD&needAddtionalResult=false&isSchoolJob=0" for i in range(1, 14): formdata = {'first': 'true', 'pn': str(i), 'kd': 'web前端'} yield scrapy.FormRequest(str(url), callback=self.parseJson, formdata=formdata)
start_requests是發送post請求的方法,FormRequest這個方法接收請求url,傳遞數據formdata,以及回調函數parseJson。parseJson在這裏主要是接收穫取的數據。ajax
僅僅有這個是不夠的,由於貌似拉勾網有反爬蟲,沒有header好像得不到數據(這個還待論證,至少我這邊是)。而後再settings.py文件中作了一些配置,配置主要有:json
DEFAULT_REQUEST_HEADERS={ Accept:application/json, text/javascript, */*; q=0.01 Host:www.lagou.com Origin:https://www.lagou.com Referer:https://www.lagou.com/jobs/list_web%E5%89%8D%E7%AB%AF?px=default&gj=3%E5%B9%B4%E5%8F%8A%E4%BB%A5%E4%B8%8B,3-5%E5%B9%B4&city=%E6%88%90%E9%83%BD User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36 }
FEED_EXPORT_ENCODING = 'utf-8'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 10
基礎的配置項配置完畢以後,就是寫數據存儲的模型了,由於我只想去簡單統計一下,因此只存了薪資和工資這兩個字段,想要統計更多的信息,就直接繼續加就行了,這個比較簡單,在items.py中編寫
class LaGou(scrapy.Item): salary = scrapy.Field() company = scrapy.Field()
通過這幾項配置,運行命令
scrapy crawl lagou -o a.json
就能夠獲得一份a.json,裏面就是成都web前端相關,工做年限爲0-5年的數據信息了。有了這份數據,接下來要作的就是數據處理了。
在以前的a.json當中,大體能夠獲得一份之下的數據,總計195條
[ {"salary": "8k-16k", "company": "xx有限公司"}, ...... ]
爲了前端處理方便,直接改成js文件加一個變量引入html,即
var a = [ {"salary": "8k-16k", "company": "xx有限公司"}, ...... ]
這組數據的薪資是一個範圍,不方便我統計,因而爲了便於操做數據把薪資取平均值,並統計提供相同的薪資的公司數目。
js代碼以下:
var arr = data.map(function (value) { return value.salary && value.salary.replace(/k|K/g, "").split('-').reduce(function (pV, nV) { return pV + nV / 2 }, 0) }).reduce(function (pV, nV) { nV in pV ? pV[nV]++ : (pV[nV] = 1); return pV; }, {}) //這裏的data既是上邊的a變量
這段代碼主要做用是把薪資範圍計算成平均數,而後再統計數組中相同的平均數的個數。代碼寫的隨意,可讀性較差,見諒。這段代碼處理事後,可獲得相似以下數據:
{'8':1,'8.5':3}
key是薪資均值,value是個數。
因而將key,value分別存入數組。這裏遇到一個問題,就是開始我是這樣操做的
var xData=[...Object.keys(arr)] var yData=[...Object.values(arr)]
這麼作有一個問題就是瀏覽器對於對象的遍歷規則,致使輸出的數組,小數都到了最外邊(好比這樣[1,2,1.5]),這樣在echarts下的圖表是亂序的。也沒有想到好的辦法去解決,就是對數組進行一次排序,而後再根據排好的key生成相對應的value數組,最終代碼:
var xData = [...Object.keys(arr).sort(function (a, b) { return a - b })] var yData = xData.map(function (v) { return arr[v] })
echarts比較簡單不贅述。將這兩組橫縱座標輸入echarts,獲得最終效果:
本次作這個統計不少地方沒想清楚怎麼更好的去表現,因此作的很簡單,其實細緻一點還能夠去分類統計,按照公司融資狀況,領域等等內容,只要數據拿到都好說。另外不少地方可能寫的不夠好,主要我目前也不太會寫,好比以前反爬蟲那塊,貌似去作動態的用戶代理也能行,但我仍是增長了延時,選擇了比較笨的方法。另外也不會python,但還好python比較好讀。由於這一塊纔開始學習,相信之後會越寫越好的,新的一年,加油!
昨天又把爬蟲優化了一下,去掉了以前的延時,增長了動態用戶代理和動態IP代理,解決了以前爬蟲的效率問題,也擴大了數據量。
經過網上搜索免費的ip代理,獲取了以下一組ip:
PROXIES = [ {'ip_port': '106.39.179.244:80'}, {'ip_port': '65.52.223.99:80'}, {'ip_port': '1.52.248.207:3128'}, {'ip_port': '45.77.198.207:3128'}, {'ip_port': '177.125.119.16:8080'}, {'ip_port': '174.138.65.233:3128'}, ]
該IP過一段時間可能會失效,請自行搜索,如http://www.xicidaili.com/。
在middlewares.py中聲明該IP,以後聲明動態IP代理類
import random class ProxyMiddleware(object): def process_request(self, request, spider): proxy = random.choice(PROXIES) request.meta['proxy'] = "http://%s" % proxy['ip_port'] print("**************ProxyMiddleware no pass************" + proxy['ip_port'])
在settings.py文件中聲明該中間件
DOWNLOADER_MIDDLEWARES = { 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110, 'tutorial.middlewares.ProxyMiddleware': 100, }
在middlewares.py中聲明動態用戶代理類
class RandomUserAgent(object): """Randomly rotate user agents based on a list of predefined ones""" def __init__(self, agents): self.agents = agents @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist('USER_AGENTS')) def process_request(self, request, spider): # print "**************************" + random.choice(self.agents) request.headers.setdefault('User-Agent', random.choice(self.agents))
一樣在settings.py的中間件裏聲明
DOWNLOADER_MIDDLEWARES = {
'tutorial.middlewares.RandomUserAgent': 1, 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110, 'tutorial.middlewares.ProxyMiddleware': 100,
}
再次運行scrapy crawl lagou,便可獲得新的數據。
在原有基礎上增長了對於工做年限和公司規模的篩選,並計算了平均值。
更新代碼以下:
// 指定圖表的配置項和數據 initData(); function initData() { average = 0; arr = temData.map(function (value) { //以前正則篩選字符串有點問題,沒有考慮到有些公司格式爲10k以上這種。 return value.salary && value.salary.replace(/[k|K\u4e00-\u9fa5]/g, "").split('-').reduce(function (pV, nV, i, array) { if (array.length > 1) { average = Number(average) + pV + nV / 2 return pV + nV / 2 } else { average = +average + Number(nV) return nV } // return array.length > 1 ? pV + nV / 2 : nV }, 0) }).reduce(function (pV, nV) { nV in pV ? pV[nV]++ : (pV[nV] = 1); return pV; }, {}) average = (average / temData.length).toFixed(2) }
暫時這樣,經過以後的學習,還會不斷的優化。
展現效果: