Python 爬虫实战(二)爬取携程(国际)机票

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秒更新一次

数据展示

猜你喜欢

转载自blog.csdn.net/blowfire123/article/details/98753720
今日推荐