Last active
May 30, 2022 18:41
-
-
Save gaukas/63eafba2efbc45283a370b7328c9e545 to your computer and use it in GitHub Desktop.
Check United Airlines' Booking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Author: Gaukas <i@gauk.as> | |
# Date: 2022-05-29 | |
# License: MIT | |
# This script mainly uses 2 API endpoints: | |
# - GET flight/upgradeListExtended (credits goes to uscardform: https://www.uscardforum.com/t/topic/82554) | |
# - POST flight/FetchFlights | |
# GET token/anonymous => x-authorization-api | |
# POST flight/FetchFlights (x-authorization-api) => []FlightInfo | |
# GET flight/upgradeListExtended (x-authorization-api, []FlightInfo) => Capacity/Authorized/Booked/Held for each cabin (Front/Middle/Rear) for each flight in []FlightInfo | |
import requests | |
import json | |
from time import sleep | |
from requests.structures import CaseInsensitiveDict | |
BASE_DOMAIN = "https://www.united.com/" | |
DEFAULT_FLIGHT_NBR = 857 | |
DEFAULT_FLIGHT_DATE = "2022-12-16" | |
DEFAULT_FROM_AIRPORT = "SFO" | |
DEFAULT_TO_AIRPORT = "PVG" | |
BOOKING_CLASSES = { | |
"ECONOMY": ["Y", "B", "M", "E", "U", "H", "Q", "V", "W", "S", "T", "L", "K", "G"], | |
"ECO-PREMIUM": ["O", "A", "R"], | |
"MIN-BUSINESS-OR-FIRST": ["J", "C", "D", "Z", "P", "I"] | |
} | |
req_url = "" | |
req_headers = CaseInsensitiveDict() | |
def set_initial_headers(): | |
req_headers["accept"] = "*/*" | |
req_headers["user-agent"] = "insomnia/2022.3.0" | |
req_headers["accept-language"] = "en-US" | |
def set_x_authorization_api(): | |
set_initial_headers() | |
if "x-authorization-api" not in req_headers: | |
req_headers["x-authorization-api"] = "bearer "+_get_token_anonymous() | |
def list_flights(Origin: str = DEFAULT_FROM_AIRPORT, Destination: str = DEFAULT_TO_AIRPORT, DepartDate: str = DEFAULT_FLIGHT_DATE, CabinPreferenceMain: str = "economy", StopCountMin: int = -1, StopCountMax: int = 0): | |
return _post_flight_fetchflights(Origin, Destination, DepartDate, CabinPreferenceMain, StopCountMin, StopCountMax) | |
def list_cabin_booking(flightNumber: int = DEFAULT_FLIGHT_NBR, flightDate: str = DEFAULT_FLIGHT_DATE, fromAirportCode: str = DEFAULT_FROM_AIRPORT): | |
return _get_flight_upgradelistextended(flightNumber, flightDate, fromAirportCode) | |
# Internal helper functions. These functions assume that all necessary headers are set and will fail if they are actually not. | |
def _get_token_anonymous(): | |
req_url = BASE_DOMAIN+"api/token/anonymous" | |
resp = requests.get(req_url, headers=req_headers, timeout=20) | |
resp_json = resp.json() | |
if "data" in resp_json: | |
if "token" in resp_json["data"]: | |
if "hash" in resp_json["data"]["token"]: | |
return resp_json["data"]["token"]["hash"] | |
else: | |
raise NameError('data.token.hash not found in response') | |
def _post_flight_fetchflights(Origin: str, Destination: str, DepartDate: str, CabinPreferenceMain: str, StopCountMin: int = -1, StopCountMax: int = 0): # Content-Type: application/json, Accept-Language: en-US, x-authorization-api: {token} | |
req_url = BASE_DOMAIN+"api/flight/FetchFlights" | |
# Inject _body_FetchFlights for fields | |
_body_FetchFlights["Trips"][0]["Origin"] = Origin | |
_body_FetchFlights["Trips"][0]["Destination"] = Destination | |
_body_FetchFlights["Trips"][0]["DepartDate"] = DepartDate | |
_body_FetchFlights["Trips"][0]["SearchFiltersIn"]["StopCountMin"] = StopCountMin | |
_body_FetchFlights["Trips"][0]["SearchFiltersIn"]["StopCountMax"] = StopCountMax | |
_body_FetchFlights["CabinPreferenceMain"] = CabinPreferenceMain | |
flights = [] | |
req_headers["content-type"] = "application/json" | |
req = requests.Request('POST',req_url,headers=req_headers,data=json.dumps(_body_FetchFlights)) | |
prepared = req.prepare() | |
s = requests.Session() | |
sleep(1) | |
resp = s.send(prepared, timeout=20) | |
resp_json = resp.json() | |
if "data" in resp_json: | |
if "Trips" in resp_json["data"]: | |
for trip in resp_json["data"]["Trips"]: | |
if "Flights" in trip: | |
for flight in trip["Flights"]: | |
flight_info = { | |
"Origin": flight["Origin"], | |
"DepartDateTime": flight["DepartDateTime"], | |
"Destination": flight["Destination"], | |
"DestinationDateTime": flight["DestinationDateTime"], | |
"BookingClassAvailability": flight["BookingClassAvailability"], | |
"Carrier": flight["MarketingCarrier"], | |
"FlightNumber": flight["FlightNumber"], | |
"Cabins": [] | |
} | |
for cabin in flight["Products"]: | |
if 'Description' not in cabin: | |
continue | |
cabin_info = { | |
"Description": cabin['Description'], | |
"Code": cabin['BookingCode'] | |
} | |
for price in cabin["Prices"]: | |
if price["PricingType"] == "Fare": | |
cabin_info["Fare"] = price["Amount"] | |
flight_info["Cabins"].append(cabin_info) | |
if "EquipmentDisclosures" in flight: | |
flight_info["EquipmentDescription"] = flight["EquipmentDisclosures"]["EquipmentDescription"] | |
flights.append(flight_info) | |
else: | |
raise NameError('data.Trips not found in response') | |
return flights | |
def _get_flight_upgradelistextended(flightNumber: int, flightDate: str, fromAirportCode: str):# Accept-Language: en-US, x-authorization-api: {token} | |
req_url = BASE_DOMAIN+"api/flight/upgradeListExtended" | |
req_params = { | |
"flightNumber": flightNumber, | |
"flightDate": flightDate, | |
"fromAirportCode": fromAirportCode | |
} | |
resp = requests.get(req_url, headers=req_headers, params=req_params, timeout=20) | |
pbts_export = CaseInsensitiveDict() | |
resp_json = resp.json() | |
if "pbts" in resp_json and len(resp_json["pbts"]) > 0: | |
for pbt in resp_json["pbts"]: | |
if "cabin" in pbt and "capacity" in pbt and "authorized" in pbt and "booked" in pbt and "held" in pbt: | |
pbt_export = {} | |
pbt_export["capacity"] = pbt["capacity"] | |
pbt_export["authorized"] = pbt["authorized"] | |
pbt_export["booked"] = pbt["booked"] | |
pbt_export["held"] = pbt["held"] | |
pbts_export[pbt["cabin"]] = pbt_export | |
return pbts_export | |
_body_FetchFlights = { | |
"SearchTypeSelection": 1, | |
"SortType": "bestmatches", | |
"SortTypeDescending": False, | |
"Trips": [ | |
{ | |
"Origin": "SFO", | |
"Destination": "PVG", | |
"DepartDate": "2022-11-01", | |
"Index": 1, | |
"TripIndex": 1, | |
"SearchRadiusMilesOrigin": "-1", | |
"SearchRadiusMilesDestination": "-1", | |
"DepartTimeApprox": 0, | |
"SearchFiltersIn": { | |
"FareFamily": "ECONOMY", | |
"AirportsStop": None, | |
"AirportsStopToAvoid": None, | |
"StopCountMax": 0, | |
"StopCountMin": -1 | |
}, | |
"UseFilters": True, | |
"NonStopMarket": True | |
} | |
], | |
"CabinPreferenceMain": "economy", | |
"PaxInfoList": [ | |
{ | |
"PaxType": 1 | |
} | |
], | |
"AwardTravel": False, | |
"NGRP": False, | |
"CalendarLengthOfStay": 0, | |
"PetCount": 0, | |
"CalendarFilters": { | |
"Filters": { | |
"PriceScheduleOptions": { | |
"Stops": 1 | |
} | |
} | |
}, | |
"Characteristics": [ | |
{ | |
"Code": "SOFT_LOGGED_IN", | |
"Value": False | |
}, | |
{ | |
"Code": "UsePassedCartId", | |
"Value": False | |
} | |
], | |
"FareType": "Refundable" | |
} | |
if __name__ == "__main__": | |
set_initial_headers() | |
set_x_authorization_api() | |
origin_airport = DEFAULT_FROM_AIRPORT | |
destination_airport = DEFAULT_TO_AIRPORT | |
flight_date = DEFAULT_FLIGHT_DATE | |
# First, list every available flight from DEFAULT_FROM_AIRPORT to DEFAULT_TO_AIRPORT on DEFAULT_FLIGHT_DATE. | |
# Here for example we list only direct flights (no connections) | |
flights = list_flights(Origin=origin_airport, Destination=destination_airport, DepartDate=flight_date) | |
if len(flights) == 0: | |
print("No flights found") | |
exit(1) | |
else: | |
print("Found {} flights from {} to {} on {}".format(len(flights), origin_airport, destination_airport, flight_date)) | |
print("") | |
for flight in flights: | |
# For each of these flights: print the flight number, the departure date and time, the destination airport code and the cabin availability. | |
print(f"{flight['Carrier']}{flight['FlightNumber']} - {flight['EquipmentDescription']}") | |
# Get the cabin booking information for each of these flights. | |
cabin_booking = list_cabin_booking(flightNumber=flight["FlightNumber"], flightDate=flight_date, fromAirportCode=origin_airport) | |
if len(cabin_booking) == 0: | |
print("No cabin booking information available yet.") | |
else: | |
print("Cabin: Capacity/Authorized/Booked/Held") | |
for cabin in cabin_booking: | |
print(f"{cabin}: {cabin_booking[cabin]['capacity']}/{cabin_booking[cabin]['authorized']}/{cabin_booking[cabin]['booked']}/{cabin_booking[cabin]['held']}") | |
for cabin in flight['Cabins']: | |
print(f"{cabin['Description']}({cabin['Code']}): ${cabin['Fare']}") | |
print(f"{flight['BookingClassAvailability']}") | |
print("=======================================") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment