Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 264 additions & 0 deletions debug_flights.html

Large diffs are not rendered by default.

77 changes: 76 additions & 1 deletion fast_flights/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,48 @@ def safe(n: Optional[LexborNode]):
strip=True
)

# Attempt to extract flight number from data-travelimpactmodelwebsiteurl attribute
flight_number = None
departure_airport = None
arrival_airport = None

url_elem = item.css_first('[data-travelimpactmodelwebsiteurl]')
if url_elem:
url = url_elem.attributes.get('data-travelimpactmodelwebsiteurl', '')
# Example: ...itinerary=JFK-LAX-F9-2503-20250801
match = re.search(r'-([A-Z0-9]+)-(\d+)-\d{8}$', url)
if match:
airline_code = match.group(1)
flight_number = match.group(2)

# Extract airport codes from the URL
# Pattern: itinerary=JFK-LAX-F9-2503-20250801
airport_match = re.search(r'itinerary=([A-Z]{3})-([A-Z]{3})-', url)
if airport_match:
departure_airport = airport_match.group(1)
arrival_airport = airport_match.group(2)

# If not found in URL, try to extract from HTML elements
if not departure_airport or not arrival_airport:
# Look for airport codes in the route information
route_elem = item.css_first('.PTuQse')
if route_elem:
route_text = route_elem.text(strip=True)
# Pattern: JFK – LAX
airport_match = re.search(r'([A-Z]{3})\s*–\s*([A-Z]{3})', route_text)
if airport_match:
departure_airport = airport_match.group(1)
arrival_airport = airport_match.group(2)

# If still not found, try looking for any span with a pattern like "AA1234", "DL567", etc.
if not flight_number:
flight_number_node = item.css_first("div.sSHqwe.tPgKwe.ogfYpf span + span")
if flight_number_node:
candidate = flight_number_node.text(strip=True)
# Simple heuristic: must contain both letters and numbers
if re.search(r'[A-Z]{2,3}\d{2,4}', candidate):
flight_number = candidate

# Get departure & arrival time
dp_ar_node = item.css("span.mv1WYe div")
try:
Expand Down Expand Up @@ -189,11 +231,44 @@ def safe(n: Optional[LexborNode]):
"stops": stops_fmt,
"delay": delay,
"price": price.replace(",", ""),
"flight_number": flight_number,
"departure_airport": departure_airport,
"arrival_airport": arrival_airport,
}
)

current_price = safe(parser.css_first("span.gOatQ")).text()
if not flights:
raise RuntimeError("No flights found:\n{}".format(r.text_markdown))
# Extract relevant parts for debugging instead of full HTML
debug_info = []

# Check if we can find the main flight containers
flight_containers = parser.css('div[jsname="IWWDBc"], div[jsname="YdtKid"]')
debug_info.append(f"Found {len(flight_containers)} flight containers")

# Check if we can find any flight items
all_flight_items = parser.css("ul.Rk10dc li")
debug_info.append(f"Found {len(all_flight_items)} flight items")

# Show first few flight items for debugging
for i, item in enumerate(all_flight_items[:3]):
name = safe(item.css_first("div.sSHqwe.tPgKwe.ogfYpf span")).text(strip=True)
debug_info.append(f"Flight item {i+1}: name='{name}'")

# Show URL element if present
url_elem = item.css_first('[data-travelimpactmodelwebsiteurl]')
if url_elem:
url = url_elem.attributes.get('data-travelimpactmodelwebsiteurl', '')
debug_info.append(f" URL: {url[:100]}...")

# Check for script data
script_elem = parser.css_first(r'script.ds\:1')
if script_elem:
debug_info.append("Found script data element")
else:
debug_info.append("No script data element found")

debug_output = "\n".join(debug_info)
raise RuntimeError(f"No flights found. Debug info:\n{debug_output}")

return Result(current_price=current_price, flights=[Flight(**fl) for fl in flights]) # type: ignore
3 changes: 3 additions & 0 deletions fast_flights/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ class Flight:
stops: int
delay: Optional[str]
price: str
flight_number: Optional[str] = None
departure_airport: Optional[str] = None
arrival_airport: Optional[str] = None
2 changes: 2 additions & 0 deletions temp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Testing flight number extraction for multiple flights...
Searching for flights from JFK to LAX on 2025-08-01...
1,424 changes: 1,424 additions & 0 deletions temp.out

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions test_airports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python3

from fast_flights import Airport, search_airport

def test_airport_functionality():
"""Test airport enum and search functionality"""
try:
print("Testing airport functionality...")

# Test direct airport access
print(f"JFK airport: {Airport.JOHN_F_KENNEDY_INTERNATIONAL_AIRPORT.value}")
print(f"LAX airport: {Airport.LOS_ANGELES_INTERNATIONAL_AIRPORT.value}")
print(f"TPE airport: {Airport.TAIWAN_TAOYUAN_INTERNATIONAL_AIRPORT.value}")

# Test airport search by name
print("\nSearching for airports containing 'New York':")
ny_results = search_airport("New York")
for airport in ny_results[:3]: # Show first 3 results
print(f" {airport.name}: {airport.value}")

print("\nSearching for airports containing 'Los Angeles':")
la_results = search_airport("Los Angeles")
for airport in la_results[:3]: # Show first 3 results
print(f" {airport.name}: {airport.value}")

print("\nSearching for airports containing 'Taipei':")
taipei_results = search_airport("Taipei")
for airport in taipei_results[:3]: # Show first 3 results
print(f" {airport.name}: {airport.value}")

print(f"\n✅ Airport functionality test successful!")
return True

except Exception as e:
print(f"❌ Airport functionality test failed: {e}")
return False

if __name__ == "__main__":
test_airport_functionality()
82 changes: 82 additions & 0 deletions test_debug_html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3

from fast_flights import FlightData, Passengers, get_flights
import re

def debug_html_structure():
"""Debug the HTML structure to find flight number selectors"""
try:
print("Debugging HTML structure for flight numbers...")

# Get the raw HTML response
from fast_flights.core import fetch, TFSData

flight_data = [
FlightData(
date="2025-08-01",
from_airport="JFK",
to_airport="LAX"
)
]

passengers = Passengers(adults=1)

filter_obj = TFSData.from_interface(
flight_data=flight_data,
trip="one-way",
passengers=passengers,
seat="economy",
)

data = filter_obj.as_b64()
params = {
"tfs": data.decode("utf-8"),
"hl": "en",
"tfu": "EgQIABABIgA",
"curr": "",
}

response = fetch(params)

# Save HTML to file for analysis
with open("debug_flights.html", "w", encoding="utf-8") as f:
f.write(response.text)

print("HTML saved to debug_flights.html")

# Look for potential flight number patterns in the HTML
html = response.text

# Search for common flight number patterns
patterns = [
r'([A-Z]{2,3}\s?\d{2,4})', # AA123, DL567, etc.
r'Flight\s+([A-Z0-9]{2,}\s?\d{2,4})', # Flight AA123
r'([A-Z]{2,3}\d{2,4})', # AA1234 (no space)
]

print("\nSearching for flight number patterns:")
for pattern in patterns:
matches = re.findall(pattern, html)
if matches:
print(f"Pattern '{pattern}' found: {matches[:5]}") # Show first 5 matches

# Look for airline names and nearby text
airline_patterns = [
r'JetBlue[^<]*',
r'American[^<]*',
r'Delta[^<]*',
r'United[^<]*',
r'Southwest[^<]*',
]

print("\nSearching for airline names and context:")
for pattern in airline_patterns:
matches = re.findall(pattern, html)
if matches:
print(f"Airline pattern '{pattern}' found: {matches[:3]}")

except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
debug_html_structure()
47 changes: 47 additions & 0 deletions test_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3

from fast_flights import FlightData, Passengers, create_filter

def test_filter_generation():
"""Test that we can generate protobuf filters correctly"""
try:
print("Testing filter generation...")

# Create flight data
flight_data = [
FlightData(
date="2025-01-15",
from_airport="JFK",
to_airport="LAX"
)
]

# Create passengers
passengers = Passengers(adults=1)

# Create filter
filter_obj = create_filter(
flight_data=flight_data,
trip="one-way",
passengers=passengers,
seat="economy"
)

# Generate base64 encoded filter
b64_filter = filter_obj.as_b64().decode('utf-8')

print(f"✅ Filter generation successful!")
print(f"Generated filter: {b64_filter}")

# Test URL generation
url = f"https://www.google.com/travel/flights?tfs={b64_filter}&hl=en"
print(f"Generated URL: {url}")

return True

except Exception as e:
print(f"❌ Filter generation failed: {e}")
return False

if __name__ == "__main__":
test_filter_generation()
49 changes: 49 additions & 0 deletions test_multiple_flights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python3

from fast_flights import FlightData, Passengers, get_flights

def test_multiple_flights():
"""Test flight number extraction for multiple flights"""
try:
print("Testing flight number extraction for multiple flights...")
print("Searching for flights from NYC to LAX on 2025-08-01...")

result = get_flights(
flight_data=[
FlightData(
date="2025-08-01",
from_airport="NYC",
to_airport="LAX"
)
],
trip="one-way",
seat="economy",
passengers=Passengers(adults=1),
fetch_mode="common",
)

print(f"✅ Search successful!")
print(f"Current price level: {result.current_price}")
print(f"Found {len(result.flights)} flights")

# Show first 5 flights with their flight numbers
print("\nFirst 5 flights with flight numbers:")
for i, flight in enumerate(result.flights[:10]):
print(f"Flight {i+1}:")
print(f" Airline: {flight.name}")
print(f" Flight number: {flight.flight_number}")
print(f" Departure: {flight.departure}")
print(f" Departure airport: {flight.departure_airport}")
print(f" Arrival: {flight.arrival}")
print(f" Arrival airport: {flight.arrival_airport}")
print(f" Duration: {flight.duration}")
print(f" Stops: {flight.stops}")
print(f" Price: {flight.price}")
print(f" Best flight: {flight.is_best}")
print()

except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
test_multiple_flights()
Loading