Python 爬蟲 獲取攜程國際航班飛機票信息(Selenium+Browsermob-proxy)

github項目:https://github.com/wzyblowfire/flightsmonitor

頁面分析

首先進入攜程網的國際機票網頁分析,可以看出該網頁是一個動態頁面,也就是說單一的請求獲取response是無法得到我們需要的數據的,所以我們需要用後臺分析一下我們真正所需要的數據到底在哪。

當搜索從香港到曼徹斯特的航班時,從Chrome控制檯Network可以看到, 有個batchSearch的請求,獲得了這一頁中的所有航班信息,這時候我試了一下直接請求這個URL,加上對應的請求頭和請求data,確實可以獲取內容。

但是隨後我發現了一個問題,如果我需要獲取別的行程的信息,比如香港去倫敦,必須自己再一次手動去複製那個搜索的headers,因爲headers中的sign和transactionid每一次搜索是唯一的,並且沒有規律可循,也無法通過請求爬取。

爲了解決需要手動複製這個問題,我使用了多種辦法,最終選擇了使用Selenium和Browsermob-proxy這兩個庫來實現。

庫安裝

1、Selenium安裝

Selenium是一個瀏覽器自動化測試工具。

命令行輸入

pip install selenium

安裝完後注意,3.x以上版本的selenium是需要手動配置webdriver的。

配置教程:https://www.cnblogs.com/lvzuwen/p/7309341.html 

記得按照自己電腦上瀏覽器的版本號下載對應版本的chromedriver.exe或geckodriver.exe。

2、Browsermob-proxy安裝

Browsermob-prox是一個 瀏覽器代理服務器工具,可以監聽瀏覽器的訪問過程。

首先電腦上必須擁有java的環境,java配置:https://www.cnblogs.com/ssrs-wanghao/articles/8994856.html

Browsermob-proxy安裝:https://blog.csdn.net/m0_37618247/article/details/85066272

記得Browsermob在github官網上release那裏下載Browsermob-proxy包:https://github.com/wzyblowfire/browsermob-proxy/releases,我下的是2.1.4版本。

代碼

(gitbub項目:https://github.com/wzyblowfire/flightsmonitor

配置環境之後,就直接上代碼了,首先是存儲航班信息的類代碼,這裏只選擇了數據中的一些信息,其實獲取的信息中有很多信息,可根據自己的需求更改。

flights.py:

# -*- coding: utf-8 -*-
"""
Created on Wed Jul 31 15:02:31 2019

@author: wzyblowfire
"""

class Flights:
    
    def __init__(self, data_dict):
        self.fid = data_dict['itineraryId'].replace(',', '-')   # 航班號
        self.name = data_dict['flightSegments'][0]['airlineName']   # 航空公司
        dur = data_dict['flightSegments'][0]['duration']   
        hour = int(dur/60)
        minute = dur%60
        self.duration = str(hour)+'h'+str(minute)+'m'   # 行時長
        self.detime = data_dict['flightSegments'][0]['flightList'][0]['departureDateTime']  # 出發時間
        self.decity = data_dict['flightSegments'][0]['flightList'][0]['departureCityName']  # 出發地
        self.arcity = []
        self.artime = []
        for x in data_dict['flightSegments'][0]['flightList']:
            self.arcity.append(x['arrivalCityName'])    # 達到城市(包括中轉)
            self.artime.append(x['arrivalDateTime'])    # 達到時間(包括中轉)  
        self.price = []
        for x in data_dict['priceList']:
            self.price.append(x['adultPrice']+x['adultTax'])    # 機票價格
        self.minprice = min(self.price)     # 最低價格
        
    def __str__(self):
        # 信息輸出
        stri = 'name: ' +self.name+'\n'+\
        'fid: '+self.fid+'\n'+\
        'detime: '+str(self.detime)+' '+str(self.decity)+'\n'+\
        'artime: '+str(self.artime)+' '+str(self.arcity)+'\n'+\
        'duration: '+self.duration+'\n'+\
        'minprice: '+str(self.minprice)
        return stri

爬蟲代碼,每60秒獲取一次信息,這裏使用的是chrome瀏覽器,由於Selenium的速度較慢,所以這裏選擇先使用Selenium獲取後續訪問batchSearch的hearders和request data,然後後續持續訪問就可以直接使用requests中的方法,提高效率,而且避免瀏覽器彈框。

selen.py:

# -*- coding: utf-8 -*-
"""
Created on Mon Aug  5 15:19:27 2019

@author: wzyblowfire
"""
import os
import time
import json
import requests
from browsermobproxy import Server 
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait  
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options

import spider
from flight import Flights

        
def search_url(depport, arrport, depdate):
    """
    獲取攜程國際機票搜索的url
    參數:
        depport:出發機場碼(機場碼可參考(https://github.com/wzyblowfire/flightsmonitor)
                            data文件夾下的world-airports.csv或
                            訪問http://ourairports.com/airports.html下載)
        arrport: 到達機場碼
        depdate: 出發日期
    返回值:
        international_url:國際航班搜索url
    """
    international_url = ('https://flights.ctrip.com/international/search/oneway-%s-%s?' + \
                        'depdate=%s&cabin=y_s&adult=1&child=0&infant=0') % (depport, arrport, depdate)
    return international_url

def get_initinfo(url):
    """
    本函數用於獲取簽名sign信息,transactionID和後續請求data.
    其中使用了selenium和browsermob-proxy.
    參數:
        url: 攜程搜索國際航班的url
    返回值:
        headers:後續請求頭信息
        postdata: 後續持續獲取航班信息請求頭中的提交json信息
    """
    # browsermob-proxy配置路徑,請將這裏填寫爲自己電腦上的路徑
    path = os.path.join('D:\code\env', 'browsermob-proxy-2.1.4','bin','browsermob-proxy.bat')   
    server = Server(path)   # 設置服務器腳本路徑
    server.start()          
    proxy = server.create_proxy()   # 創建一個瀏覽器代理
    
    # chrome測試配置
    chrome_options = Options()
    chrome_options.add_argument('--ignore-certificate-errors')
    chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
    chrome_options.add_argument('--disable-gpu')     
    driver = webdriver.Chrome(chrome_options = chrome_options)  # 使用selenium創建瀏覽器窗口
    proxy.new_har(url, options={'captureContent':True, 'captureHeaders':True})  # 代理服務器開始監測,捕捉文本和請求頭信息
    driver.get(url)
    # 顯示等待5秒,因爲網頁會持續加載,需要等待一段時間,直到航空公司內容出現,說明加載成功
    WebDriverWait(driver,5,0.5).until(EC.presence_of_element_located((By.CLASS_NAME,'airline-name'))) 
    
    result = proxy.har
    
    server.stop()
    driver.quit()
    # 獲取https://flights.ctrip.com/international/search/api/search/batchSearch這個訪問過程中的重要信息
    headers = {}
    for entry in result['log']['entries']:
        if 'batchSearch' in entry['request']['url']:
            postdata = entry['request']['postData']['text']
            header = entry['request']['headers']  
           
            for x in header:
                headers[x['name']] = x['value']         
    return headers, postdata

def spider_searchflights(headers, post_data):
    """
    後續持續獲取數據函數
    參數:
        headers:請求頭信息
        post_data: 請求頭中的數據信息(json)
    返回:
        dict_json: 航班信息(字典)
    """
    
    search_URL = 'https://flights.ctrip.com/international/search/api/search/batchSearch?v='
    response = requests.post(search_URL, data=post_data, headers=headers)
    dict_json = json.loads(response.text)
    # 如果請求不成功,輸出信息
    if dict_json['status'] != 0:
        print(dict_json['msg'])
    return dict_json


if __name__ == '__main__':
    url = search_url('hkg', 'man', '2019-09-20')
    headers, postdata = get_initinfo(url)
    postdata = json.loads(postdata)
    postdata = json.dumps(postdata)
    while True:
        result = spider.spider_searchflights(headers, postdata)
        result = result['data']['flightItineraryList']
        flights = {}
        for x in result:
            flight = Flights(x)
            print(flight)
        time.sleep(60)  #每60秒更新一次

數據展示

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章