Skip to main content
Glama
xiaonieli7

Flight Ticket MCP Server

by xiaonieli7
flight_transfer_tools.py9.42 kB
""" Flight Transfer Tools - 航班中转查询工具 提供根据始发地、中转地、目的地查询飞机中转方案。 """ from datetime import datetime, timedelta from typing import Dict, List, Optional import logging from selenium import webdriver from selenium.webdriver import Keys from selenium.webdriver.common.by import By import time from ..core.flights import FlightSchedule, FlightPrice, Flight, SeatConfiguration, FlightTransfer # 初始化日志器 logger = logging.getLogger(__name__) def getTransferFlightsByThreePlace(from_place: str, transfer_place: str, to_place: str,departure_date: str, min_transfer_time: float = 2.0, max_transfer_time: float = 5.0) -> List[FlightTransfer]: """ 查询从出发地通过中转地到目的地的联程航班信息。 Args: from_place (str): 出发地城市或机场 transfer_place (str): 中转地城市或机场 to_place (str): 目的地城市或机场 min_transfer_time (float): 最小中转时间(单位:小时),默认 2 小时 max_transfer_time (float): 最大中转时间(单位:小时),默认 5 小时 Returns: List[str]: 符合条件的航班列表,每个航班用字典表示。 """ logger.info(f"开始查询中转航班...") logger.info(f"始发地: {from_place},中转地:{transfer_place}, 目的地: {to_place}") try: # 获取所有城市的三字码 from_code = _get_location_codev2(from_place) transfer_code = _get_location_codev2(transfer_place) to_code = _get_location_codev2(to_place) logger.info(f"三字码查询成功!始发地: {from_code},中转地{transfer_code}, 目的地: {to_code}") # 获取两段行程列表 first_trips = _get_direct_airline(from_code, transfer_code) after_trips = _get_direct_airline(transfer_code, to_code) logger.info(f"行程分段查询成功! {from_place} - {transfer_place} {len(first_trips)}") logger.info(f"{transfer_place} - {to_place} {len(after_trips)}") # 计算换乘路线 select_trips = [] index=1 for trip1 in first_trips: arrival_time = trip1.schedule.arrival_time arrival_time = datetime.strptime(arrival_time, "%H:%M").time() arrival_time = datetime.combine(datetime.today(), arrival_time) for trip2 in after_trips: departure_time = trip2.schedule.departure_time departure_time = datetime.strptime(departure_time, "%H:%M").time() departure_time = datetime.combine(datetime.today(), departure_time) if (departure_time - arrival_time > timedelta(hours=min_transfer_time) and departure_time - arrival_time < timedelta(hours=max_transfer_time)): # 符合换乘时间要求,添加到结果列表 logger.info(f"符合换乘时间要求: {trip1.flight_number} {arrival_time} - {trip2.flight_number} {departure_time}") transfer=FlightTransfer( transfer_id=f"{index}", first_flight=trip1, second_flight=trip2, departure_date=departure_date, transfer_time=round((departure_time - arrival_time).total_seconds() / 3600,3) ) index += 1 logger.info(f"添加中转航班: {transfer.first_flight.flight_number} -> {transfer.second_flight.flight_number}, 中转时间: {transfer.transfer_time}小时") select_trips.append(transfer) logger.info(f"查询到 {len(select_trips)} 条中转航班信息") return select_trips except Exception as e: logger.warning(f"查询中转航班信息失败:{from_place}-{transfer_place}-{to_place}, 错误: {str(e)}", exc_info=True) def _get_location_code(place: str) -> str: ''' 获取城市对应的机场三字码(IATA Code)。 Args: place (str): 城市名称,例如 "北京"、"Shanghai" Returns: Optional[str]: 对应的机场三字码,如 "PEK" 或 "PVG";如果找不到则返回 None。 ''' options = webdriver.ChromeOptions() options.add_argument('--headless') # 无头模式,不打开浏览器窗口 driver = webdriver.Chrome(options=options) try: url = 'http://szdm.00cha.net/' driver.get(url) time.sleep(1) input_box = driver.find_element(By.NAME, "txtname") input_box.clear() input_box.send_keys(place) search_button = driver.find_element(By.ID, "btnQuery") search_button.click() time.sleep(1) results = driver.find_elements(By.CLASS_NAME, "tabled") code = "" if len(results) == 0: logger.warning("输入城市名错误!") else: text = results[0].text code_array = [line.strip().split() for line in text.strip().splitlines()][1:] country = code_array[0][-1] select_array = [item for item in code_array if item[-1] == country] sorted_codes = sorted(select_array, key=len) code = sorted_codes[0][0] return code[:3] except Exception as e: logger.warning(f"查询{place}城市三字码错误" + str(e)) finally: driver.close() def _get_location_codev2(place: str) -> str: ''' 获取城市对应的机场三字码(IATA Code)。 Args: place (str): 城市名称,例如 "北京" Returns: Optional[str]: 对应的机场三字码,如 "PEK" 或 "PVG";如果找不到则返回 None。 ''' options = webdriver.ChromeOptions() options.add_argument('--headless') # 无头模式,不打开浏览器窗口 driver = webdriver.Chrome(options=options) try: url = 'https://www.chahangxian.com/' # 示例:百度汉语 # 打开网页 driver.get(url) time.sleep(2) # 输入一个字 search_box = driver.find_element(By.CLASS_NAME, "search") # 百度汉语的输入框ID是kw input_box = search_box.find_element(By.NAME, "keyword") # 百度汉语的输入框ID是kw input_box.clear() input_box.send_keys(place) input_box.send_keys(Keys.ENTER) time.sleep(2) return driver.current_url.split("/")[-2] except Exception as e: logger.warning(f"查询{place}城市三字码错误" + str(e)) finally: driver.close() def _get_direct_airline(from_code: str, to_code: str) -> list: ''' :param from_code: :param to_code: :return: ''' options = webdriver.ChromeOptions() options.add_argument('--headless') # 无头模式,不打开浏览器窗口 driver = webdriver.Chrome(options=options) try: url = f"https://www.chahangxian.com/{from_code.lower()}-{to_code.lower()}/" driver.get(url) time.sleep(1) tabs = driver.find_elements(By.CLASS_NAME, "J_link") # 修改为你目标网站的内容类名 if len(tabs) == 0: logger.warning(f"航班为空 {from_code}-{to_code}") else: result = [] index=1 for tab in tabs: transfer = tab.find_elements(By.CLASS_NAME, "transfer") if len(transfer) == 0: box = tab.find_element(By.CLASS_NAME, "airline-box") img = box.find_element(By.TAG_NAME, 'img') airline = img.get_attribute('alt') message = tab.text.splitlines() schedule = FlightSchedule( departure_time=message[3], arrival_time=message[7], duration="", timezone="" ) mPrice = message[13].split(" ")[1].split("~") price = FlightPrice( economy=float(mPrice[0]), business=float(mPrice[-1]), first=0, ) flight = Flight( flight_id=f"{index}", flight_number=message[0], airline=airline, aircraft=message[1], origin=message[4], destination=message[8], schedule=schedule, price=price, seat_config=SeatConfiguration(), services={}, ) index += 1 result.append(flight) if len(result) == 0: logger.warning("没有直飞,建议转机") else: # print(len(result), result) return result except Exception as e: logger.warning(f"直飞查询失败 {from_code}-{to_code}" + str(e)) finally: driver.close() if __name__ == '__main__': # project_root = os.path.dirname(os.path.abspath(__file__)) # sys.path.insert(0, project_root) print("开始查询中转航班") # 示例查询 results = getTransferFlightsByThreePlace("北京", "迪拜", "维也纳") print(results)

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xiaonieli7/FlightTicketMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server