This repository has been archived by the owner on Apr 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SouthwestNonstop.py
208 lines (183 loc) · 7.83 KB
/
SouthwestNonstop.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# SWA Flight Schedule Scraper // Nick Alvarez c 2021
# Functional. They don't care about scraping flight schedules.
from datetime import date, timedelta
from progressbar import progressbar
import queue
import subprocess
import logging
import calendar
import time
import signal
import requests
import sys
import threading
from requests.models import to_native_string
from selenium.webdriver.remote.remote_connection import LOGGER as seleniumLogger
seleniumLogger.setLevel(logging.WARNING)
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
# Keyboard interrupts
def exitHandler(signum, frame):
exit(0)
# Chrome options to disable logging in terminal
signal.signal(signal.SIGINT, exitHandler)
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_experimental_option("excludeSwitches", ["enable-logging"])
to_driver = webdriver.Chrome(service_log_path='NULL', options=chrome_options)
rtn_driver = webdriver.Chrome(service_log_path='NULL', options=chrome_options)
# I hate global variables
search_date = date.today()
return_date = date.today()
direct_enabled = False
def main():
global search_date, return_date
# User input for origin/destination
IATA_origin = input("Origin: ").upper()
IATA_destination = input("Destination: ").upper()
# Find last bookable date from 'data.js'
data = requests.get("https://www.southwest.com/swa-ui/bootstrap/air-flight-schedules/1/data.js").text.strip().split("\n")
data = [i for i in data if "currentLastBookableDate" in i]
last_bookable_date = data[0].replace(" ","").replace('"currentLastBookableDate":"',"").replace('",',"").split("-")
end_date = date(int(last_bookable_date[0]), int(last_bookable_date[1]), int(last_bookable_date[2]))
one_day_delta = timedelta(days=1)
return_date = end_date
print(f"\nFlight schedules can be seen up to {end_date}.")
# User input for travel dates if not in 'all' mode
def datesInput():
global search_date, return_date
search_date = input("Departure date (YYYY-MM-DD): ")
return_date = input("Return date (YYYY-MM-DD): ")
search_date = date(int(search_date.split("-")[0]), int(search_date.split("-")[1]), int(search_date.split("-")[2]))
return_date = date(int(return_date.split("-")[0]), int(return_date.split("-")[1]), int(return_date.split("-")[2]))
while (return_date > end_date) or (search_date < date.today()):
print(f"Error! Impossible schedule. Flight schedules can be seen up to {end_date}.")
datesInput()
print()
# Checks for nonstop flights from origin to destination
def roundtrip(orig, dest, driver):
URL = f"https://www.southwest.com/air/flight-schedules/results.html?departureDate={search_date}&destinationAirportCode={dest}&originationAirportCode={orig}&scheduleViewType=daily&timeOfDay=ALL_DAY"
global direct_enabled
finalAnswer = ""
try:
driver.get(URL)
try:
try: # Makes sure page is loaded, else times out
WebDriverWait(driver,5).until(EC.presence_of_element_located((By.XPATH, "//div[@class='accordion']")))
except TimeoutException:
pass
# Gather list of flights
flights = driver.find_element_by_xpath("//div[@class='accordion']")
flight_list = flights.find_elements_by_css_selector("div.accordion-panel")
#def gatherInfo(flight_num, flight_time):
# flight_num = flight.find_element_by_xpath(".//span[@aria-hidden='true']").text
# flight_num = flight_num.replace(" ", "")
# flight_time = flight.find_elements_by_xpath(".//span[@class='time--value']")
# For each flight, check if it is nonstop (it will cause exception otherwise), gather flight info
for flight in flight_list:
flight_num = ""
flight_time= []
try:
if direct_enabled:
try:
if "no plane change" == flight.find_element_by_xpath(".//span[@class='flight-stops--item-description flight-stops--item-description_one-stop']").text.lower():
search = "Direct"
flight_num = flight.find_element_by_xpath(".//span[@aria-hidden='true']").text
flight_num = flight_num.replace(" ", "")
flight_time = flight.find_elements_by_xpath(".//span[@class='time--value']")
except:
search = flight.find_element_by_xpath(".//span[@class='flight-stops--item-description_nonstop']").text
flight_num = flight.find_element_by_xpath(".//span[@aria-hidden='true']").text
flight_num = flight_num.replace(" ", "")
flight_time = flight.find_elements_by_xpath(".//span[@class='time--value']")
else:
search = flight.find_element_by_xpath(".//span[@class='flight-stops--item-description_nonstop']").text
flight_num = flight.find_element_by_xpath(".//span[@aria-hidden='true']").text
flight_num = flight_num.replace(" ", "")
flight_time = flight.find_elements_by_xpath(".//span[@class='time--value']")
response = f"{calendar.day_abbr[search_date.weekday()]}\t{search_date}\t{orig}-{dest}\t{flight_num}\t{flight_time[0].text}\t{flight_time[1].text}"
if direct_enabled:
response += f"\t{search}"
finalAnswer += f'{response}\n'
except:
pass
except:
pass
return finalAnswer
# Ctrl-C
except IOError:
pass
# Checks every day until it reaches end of booking results
def searchAndPrint(count=2):
global search_date, return_date
print(f"----------------------{search_date}----------------------")
def goingToFlights(q):
if (count == 2) or (count == 0):
t = roundtrip(IATA_origin, IATA_destination, to_driver)
q.put(t)
event_t1.set()
def comingFromFlights(q):
if (count == 2) or (count == 1):
r = roundtrip(IATA_destination, IATA_origin, rtn_driver)
q.put(r)
event_t2.set()
q1 = queue.Queue()
q2 = queue.Queue()
t1 = threading.Thread(target=goingToFlights, args=[q1])
t2 = threading.Thread(target=comingFromFlights, args=[q2])
t1.daemon = True
t2.daemon = True
event_t1 = threading.Event()
event_t2 = threading.Event()
t1.start()
t2.start()
t1.join()
t2.join()
event_t1.wait()
event_t2.wait()
print(f'{q1.get()}{q2.get()}')
# If we are not in 'all' or 'interval' mode, do not increment the day, go to the return date
try:
if mode != None:
search_date += one_day_delta
except NameError:
search_date = return_date
# Checking for command line arguments
try:
global direct_enabled
try:
for arg in sys.argv:
if 'direct' == arg:
direct_enabled = True
continue
except IndexError:
pass
mode = sys.argv[1]
if mode == 'all':
#while search_date < end_date: # Up to last booking day
days = end_date-search_date
for i in progressbar(range(days.days), redirect_stdout=True):
searchAndPrint()
elif mode == 'interval':
datesInput()
days = return_date-search_date
#while search_date <= return_date: # Up to return day
for i in progressbar(range(days.days), redirect_stdout=True):
searchAndPrint()
elif mode == 'direct':
datesInput()
for d in progressbar(range(0,2), redirect_stdout=True): # Only two days
searchAndPrint(d)
except IndexError:
datesInput()
for d in progressbar(range(0,2), redirect_stdout=True): # Only two days
searchAndPrint(d)
if __name__ == "__main__":
try:
main()
except ConnectionResetError:
exit()