爬取深圳航空航線相關(guān)數(shù)據(jù)

最近在找工作,說多了都是淚,自己挺喜歡寫爬蟲的,所以想找一份爬蟲的職業(yè),無奈是轉(zhuǎn)行并且學(xué)歷大專并不夠硬,以至現(xiàn)在還是沒能如愿,爬取航空數(shù)據(jù)這是其中一家面試公司給的面試題,給了7天的時(shí)間去完成,用了三天的時(shí)間已經(jīng)完成了,結(jié)果也已經(jīng)交付了,再等通知,希望能過,不過感覺還是希望不大,只能繼續(xù)加油了,寫這份代碼的時(shí)候走了不少彎路,所以寫下這篇博客記錄下來
1、目標(biāo)地址:深圳航空機(jī)票預(yù)訂頁面http://www.shenzhenair.com/szair_B2C/flightsearch.action?hcType=DC&orgCity=%E5%8C%97%E4%BA%AC&orgCityCode=PEK&dstCity=%E6%B7%B1%E5%9C%B3&dstCityCode=SZX&orgDate=2019-03-08&dstDate=2019-03-08
在這里插入圖片描述
從這個(gè)頁面可以看到航班信息的數(shù)據(jù),所有航班的價(jià)格都在所有價(jià)格里面,點(diǎn)開可以看到
2、我們查看一下網(wǎng)頁源代碼是沒有任何我們要找的數(shù)據(jù)的,因?yàn)椴樵兒桨嘈枰峤槐韱?,提交表單常用的方式就是post,所以我們就可以判斷這是通過異步加載出來的數(shù)據(jù),具體的數(shù)據(jù)我就不找了,這是一個(gè)json格式的數(shù)據(jù),里面的數(shù)據(jù)大家感興趣可以自己找找
在這里插入圖片描述
3、在network里面可以看到一個(gè)post方式傳送的鏈接,打開之后可以看到每個(gè)航班的數(shù)據(jù)都在 flightInfoList 中
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
4、接下來我們分析這個(gè)網(wǎng)站的反爬機(jī)制,在每次刷新頁面或者切換其他日期是在Cookie中的 PV對(duì)應(yīng)的值 會(huì) 加 1,所以在程序中也需要在每次請(qǐng)求之后需要對(duì)這個(gè)值進(jìn)行加1操作,初始值可以隨意設(shè)置,建議20以內(nèi), 另外 Cookie有過期時(shí)間,時(shí)間過期之后需要更新程序中的Cookie,并且每隔一段時(shí)間之后,session也會(huì)過期,此時(shí)也需要更新Cookie值,并且Seeion更新之后,PV對(duì)應(yīng)的值也會(huì)重 1 開始累加,最后還有一點(diǎn),每次我們刷新頁面時(shí),會(huì)需要延遲一點(diǎn)時(shí)間,頁面才會(huì)加載出來,此時(shí)就需要在程序里面請(qǐng)求一次最好睡眠一兩秒的時(shí)間
在這里插入圖片描述
6、再返回來看一下post方式提交的數(shù)據(jù),在form_data里面可以看到,每次查看不同日期的數(shù)據(jù)時(shí),日期也會(huì)隨之改變,所以在程序里面,我用了time模塊,讀取了當(dāng)前的日期并且格式化form_data日期的格式,每次請(qǐng)求時(shí)傳遞進(jìn)去,響應(yīng)完數(shù)據(jù)處理之后進(jìn)行加1,為下一次請(qǐng)求賦值,這樣就可以請(qǐng)求到不同日期的數(shù)據(jù)了(這還有個(gè)BUG,因?yàn)檎?qǐng)求日期大于這個(gè)月最大日期后就會(huì)報(bào)錯(cuò),如:20190332>20190331,這個(gè)bug大家可以自行解決,我暫時(shí)沒做處理)
在這里插入圖片描述
6、最后附上所有的代碼
# coding=gbk

'''
反爬機(jī)制: 檢測(cè)當(dāng)前IP + 請(qǐng)求頭 + cookie
    判斷cookie:有過期時(shí)間, 過期后更新  self.AlteonP  self.sign_flight
    判斷sessionid:sessionid過期 更新 整個(gè)cookies 或者 JSsessionid
    每請(qǐng)求一次  cookie中 PV值 加 1
'''
import requests, time, random, json,logging

class ShenZhenAir:
    def __init__(self):

        self.url = 'http://www.shenzhenair.com/szair_B2C/flightSearch.action'

        # 日期的請(qǐng)求時(shí)添加  并且方便下一次更新調(diào)用
        self.form_data = {
            'condition.orgCityCode': 'PEK',
            'condition.dstCityCode': 'SZX',
            'condition.hcType': 'DC',
        }
        # referer 信息也在請(qǐng)求時(shí)添加,需要更新 post 傳遞參數(shù),User-Agent不能修改,因?yàn)閁ser-agent綁定cookie+IP
        self.headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Content-Length': '129',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Host': 'www.shenzhenair.com',
            'Origin': 'http://www.shenzhenair.com',
            'Proxy-Connection': 'keep-alive',
            'X-Requested-With': 'XMLHttpRequest',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36',
        }
        # cookie值 值取到 PV: 因?yàn)槊看握?qǐng)求需要 加1 操作
        self.cookie = '自己的cookie值pv:'

        # refer值 也需要拼接,所以只取到中間一部分 日期后面的部分在請(qǐng)求時(shí)拼接進(jìn)去
        self.refer = 'http://www.shenzhenair.com/szair_B2C/flightsearch.action?orgCityCode=PEK&dstCityCode=SZX&orgDate='


    def getJson(self):
        # print(self.headers)
        n = 1
        p = 18
        n_time = time.localtime()
        base_time = int(time.strftime('%Y%m%d', n_time))
        try:
            while n<=7:
                # 時(shí)間更新用
                date = str(base_time)[0:4] + '-' + str(base_time)[4:6] + '-' + str(base_time)[6:]

                dstDate = base_time + 1
                conditiondstDate = str(dstDate)[0:4] + '-' + str(dstDate)[4:6] + '-' + str(dstDate)[6:]
                # 更新 傳遞的 data
                self.form_data['condition.orgDate'] = date
                self.form_data['condition.dstDate'] = conditiondstDate
                # 更新 請(qǐng)求頭  信息
                self.headers['Referer'] = self.refer + date + '&hcType=DC'
                self.headers['Cookie'] = self.cookie + str(p)

                print('正在獲取%s號(hào)信息' % date)
                # 發(fā)起請(qǐng)求獲取數(shù)據(jù)
                res = requests.post(self.url, headers=self.headers, data=self.form_data)
                time.sleep(5)
                print(res.request.headers['Cookie'])
                # print(res.cookies)
                # res.encoding = 'utf-8'
                html = json.loads(res.text)
                print('==' * 30)

                # 對(duì)獲取的數(shù)據(jù)進(jìn)行解析
                self.parseJson(html)

                # 數(shù)值更新
                n += 1
                p += 1
                base_time += 1
                time.sleep(0.5)
        except json.decoder.JSONDecodeError:
            print('后續(xù)處理')

    def parseJson(self, html):
        '''
        對(duì)獲取的 Json數(shù)據(jù)進(jìn)行解析
        :param html:
        :return:
        '''
        if len(html):
            info = html['flightSearchResult']['flightInfoList']
            for i in info:
                flightno = i['flightNo']
                orgdate = i['orgDate']
                orgtime = i['orgTime']
                dsttime = i['dstTime']
                orgcitych = i['orgCityCH']
                dstcitych = i['dstCityCH']

                print('去程: '+orgcitych +'-'+dstcitych + ' '+'機(jī)型: {}, 起飛日期: {}, 起飛時(shí)間: {}, 落地時(shí)間: {}'.format(flightno, orgdate, orgtime, dsttime))
                classinfolist = i['classInfoList']
                for j in classinfolist:
                    class_type = j['classCode']
                    class_price = j['classPrice']
                    print(class_type + '艙' + ': ' + class_price + '元')
                print()
            time.sleep(0.5)
        else:
            print('抱歉,該日期無座位或航班')

    def main(self):
        self.getJson()

if __name__ == '__main__':
    app = ShenZhenAir()
    app.main()
7、注意:說一下我的一些經(jīng)驗(yàn),因?yàn)樵谂廊∵@個(gè)網(wǎng)站時(shí),沒特別注意cookie的值,所以,而且可能傳遞的參數(shù)也有問題,所以請(qǐng)求之后返回的數(shù)據(jù)只是html,并不是json數(shù)據(jù),一開始我以為是數(shù)據(jù)加密了,所以就想著從公眾號(hào)的接口獲取數(shù)據(jù),代碼也寫完了,但是最多也只能獲取兩天的數(shù)據(jù),我還以為是反爬機(jī)制比較狠,所以通過更換IP來解決,但是更換IP之后還是沒有解決,我又以為是IP不好用,又買了動(dòng)態(tài)轉(zhuǎn)發(fā),結(jié)果還是不如意,最后沒辦法,還是回到最初靜下心來去研究網(wǎng)頁版的反爬機(jī)制,當(dāng)我成功獲取到連續(xù)的數(shù)據(jù)才發(fā)現(xiàn)自己真的有點(diǎn)傻,反爬機(jī)制原來就這么簡(jiǎn)單,我卻走了這么多彎路,不夠這次就當(dāng)是對(duì)自己的一個(gè)教訓(xùn)了,以后還是能先研究明白網(wǎng)站的反爬機(jī)制,再去對(duì)癥下藥吧
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容