Python | 數據分析實戰Ⅰ

Linux之父Linus

「Talk is cheap,Show me the code.」翻譯爲中文是「廢話少說,放碼過來。」我以爲可謂信達雅。javascript

在編程之路上,實踐的重要性無可比擬。這也是不少同窗感受學了不少,但仍是不會寫代碼的緣由;也是不少有意轉行的人士,自學了大半年,仍不見轉機的緣故。java

leoxin在知識星球發起一項活動:目標是爬取拉鉤網的招聘信息以及鏈家的房產信息,而後對數據進行清洗和存儲,並分析其數據下的價值,最後用可視化的形式表現出來。 我以爲難度適中,按部就班,對於不一樣身份角色的學習人羣都大有裨益。git

下面我來寫一寫在第一階段的一些學習操做總結和感覺。web

爬蟲

構思/準備部分

首先打開拉鉤網,我初步選擇的是Java-上海 編程

Step1

打開瀏覽器開發者工具,觀察Network部分的內容,首先點進Doc部分,看看服務器給咱們返回了哪些文本內容。json

在Preview預覽中,咱們能夠看到,大部分都是一些公共視圖框架和公共JS代碼,沒有核心數據。數組

那麼這時咱們就應該猜想,網站多是首先渲染一個公共框架,而後再經過Ajax發送請求去得到數據,再在頁面上顯示獲取的數據。 瀏覽器

doc

Step2

經過Step1的思考和猜想,大體肯定了數據是異步獲取的。作過一點web的應該都想獲得這一點,由於即便是反爬,也要按照基本法啊!應該不會使用多麼多麼匪夷所思的黑科技(若是真的出現了,那是我太菜了的緣由(っ °Д °;)っ)bash

這時點開XHR按鈕,XHR全稱XMLHttpRequest,有什麼做用呢?Ajax經過XMLHttpRequest對象發出HTTP請求,獲得服務器返回的數據。服務器

經過預覽咱們能夠發現,咱們須要的數據都出如今positionAjax請求下返回的數據中,參見content-positionResult-result中。

那麼該如何僞造請求?

點進Headers,首先發現Request Method的值是POST,肯定了咱們提交請求的方式。而後看看Request Headers中的字段。

發現有好多條。

Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9
Connection:keep-alive
Content-Length:23
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:XXX
Host:www.lagou.com
Origin:https://www.lagou.com
Referer:https://www.lagou.com/jobs/list_Java?px=default&city=%E4%B8%8A%E6%B5%B7
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
X-Anit-Forge-Code:0
X-Anit-Forge-Token:None
X-Requested-With:XMLHttpRequest
複製代碼

經過篩選,咱們選取其中有用的幾條,構建本身的請求頭。(那麼怎麼知道哪些是有用的呢?首先篩除語言,編碼之類的,這些的影響通常都是比較小的;接着在剩下的字段中進行嘗試,等之後有經驗了,天然能準確選取有價值的字段)

因爲是POST的請求方式,那麼勢必會向服務器提交一些數據,能夠看到表單信息:

first:true
pn:1
kd:Java
複製代碼

xhr
headers

實現/代碼部分

Step1

在進行上述分析後,基本已經準備得差很少了。這時能夠先簡單構建一下咱們的Proxy類。

class Proxy():
    def __init__(self):
        self.MAX=5 #最大嗅探次數
        self.headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
            "Referer":"https://www.lagou.com/jobs/list_Java?px=default&city=%E4%B8%8A%E6%B5%B7",
            "X-Anit-Forge-Code":"0",
            "X-Anit-Forge-Token":"None",
            "X-Requested-With":"XMLHttpRequest"
        }

    def getPage(self,url,data):
        FAILTIME=0 #訪問失敗次數
        try:
            result=requests.post(url,headers=self.headers,data=data)
            result.encoding = "utf-8"
            return result
        except:
            FAILTIME+=1
            if FAILTIME==self.MAX:
                print("訪問錯誤")
                return ''

複製代碼

上文中提到,發現Ajaxposition返回的content-positionResult-result數據,數據格式是一個數組裏有15條數據,每條數據的格式是一個字典,具體以下:

adWord:9
appShow:0
approve:1
businessZones:["唐鎮", "唐鎮", "曹路", "曹路"]
city:"上海"
companyFullName:"招商銀行股份有限公司信用卡中心"
companyId:6796
companyLabelList:["金融科技銀行", "技術創新驅動", "奮鬥獨立改變", "一年兩次調薪"]
companyLogo:"i/image2/M00/25/D7/CgoB5lodmL2AJHxrAABieRjcJjU514.png"
companyShortName:"招商銀行信用卡中心"
companySize:"2000人以上"
createTime:"2018-03-09 09:14:30"
deliver:0
district:"浦東新區"
education:"本科"
explain:null
financeStage:"上市公司"
firstType:"開發/測試/運維類"
formatCreateTime:"09:14發佈"
gradeDescription:null
hitags:null
imState:"today"
industryField:"移動互聯網,金融"
industryLables:[]
isSchoolJob:0
jobNature:"全職"
lastLogin:1520581074000
latitude:"31.247248"
linestaion:null
longitude:"121.673868"
pcShow:0
plus:null
positionAdvantage:"五險一金,職位晉升,各種補貼"
positionId:2762378
positionLables:["項目管理", "j2ee", "架構"]
positionName:"Java技術經理"
promotionScoreExplain:null
publisherId:73936
resumeProcessDay:1
resumeProcessRate:100
salary:"30k-50k"
score:0
secondType:"管理崗"
stationname:null
subwayline:null
workYear:"5-10年"

複製代碼

可見返回了大量的信息,那麼咱們如何去得到這些數據?此時能夠寫一個簡單的Job類:

class Job:
    def __init__(self):
        self.datalist=[]

    def getJob(self,url,data):
        p=Proxy()
        result=p.getPage(url,data)
        result.encoding = "utf-8"
        result_dict=result.json()
        try:
            job_info = result_dict['content']['positionResult']['result']
            for info in job_info:
                print(info)
            return job_info
        except:
            print("發生解析錯誤")
       
複製代碼

使用getJob方法獲取的是全部的信息,這實際上是沒必要要的,應該也有所選擇,不然將給本身帶來壓力,對於後續步驟也將帶來不便。

Step2

此時,一個簡單的爬蟲已經編寫得差很少了,咱們能夠進行測試一下。 編寫主函數

if __name__ == '__main__':
    url="https://www.lagou.com/jobs/positionAjax.json?px=default&city=%E4%B8%8A%E6%B5%B7&needAddtionalResult=false&isSchoolJob=0"
    job = Job()
    all_page_info=[]
    for x in range(1,31):
        data = {
            "first": "false",
            "pn": x,
            "kd": "Java"
        }
        current_page_info=job.getJob(url,data)
        all_page_info.extend(current_page_info)
        print("第%d頁已經爬取成功"%x)
        time.sleep(5)
複製代碼

能夠看到控制檯顯示:

總結

到這裏,一個簡單的爬蟲已經編寫完畢了,數據以json格式返回,彷佛已經大功告成。而事實是,對於爲後面的數據分析作準備工做還差得遠,對於爬取海量數據,下面有幾點思考。

  • 拉鉤網對於同一ip的大量請求行爲確定會進行封禁,因此須要準備代理池。
  • 爲了實現高自動化,須要對一系列可能出現的異常狀況進行處理,斷點處理,確保程序不掛。
  • 爲了提升效率,加入多線程。
  • 數據持久化,在持久化以前須要先進行清洗。
  • ......

針對以上問題,須要進一步學習。

掃一掃,關注公衆號
相關文章
相關標籤/搜索