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秒更新一次
數據展示