Flightstats API: Пишем свое табло прилетов с Боингами и Аэробусами

  • Tutorial

Введение


Всем читающим этот пост — здравствуйте.
Авиация — мое хобби, я об этом уже писал. Я готов часами стоять и фотографировать самолеты, смотреть видео о них, читать блоги летчиков, следить за трафиком на fr24.com. А еще мне нравится то, что в авиации кругом одни сокращения: ECAM, CDA, ACESS, APU и так далее. Вообщем, магия. А вот почти все люди из моего окружения к авиации равнодушны: «Ну самолет, как самолет. Большой, да. Что? Boeing 777-300ER? Ну ясно, ясно...». Но ничего не поделаешь, на вкус и цвет все фломастеры разные.
Как ко мне пришла идея поста? Так получилось, что я живу в 20 минутах езды от аэропорта Шереметьево. Как-то у меня выдался свободный час и приехал немного пофотографировать. За то время, пока я там был, мимо меня пролетело около 10 самолетов. Почти все — Аэрофлот. Я не спорю, у Аэрофлота есть интересные борты. Например, Добролет или Хохлома. Но в тот день мне не повезло, ничего подобного я не увидел. И тогда я подумал, что было бы очень полезно планировать подобные выезды. Вот так вот и родилась идея поста. Мне хотелось иметь следующий функционал: таблица вылетов — прилетов для выбранного аэропорта, выделение цветом как интересных, так и не очень ботов, экспорт в pdf.

Начинаем!

Итак, первым делом необходимо зайти на https://developer.flightstats.com, зарегистрироваться, перейти в Dashboard и нажать на кнопку «Create a new application». Это необходимо для получения связки AppId + AppKey, без которой доступ к API невозможен. Вообще, оно платное, но присутствует и бесплатный тарифный план — "Evaluation Plan", его возможностей для наших нужд хватит сполна. После этого смело идем "Get Started" -> "Flex API Reference" -> "Flight Status & Track API" -> "Flight Status & Track by Airport". В нижней части страницы есть раздел "Interactive Documentation", выбираем "Airport status (departures)". В данном запросе есть 7 обязательных полей, которые необходимо заполнить следующим образом:
appId appKey airport year month day hourOfDay
Ваш appId Ваш appKey SVO 2013 12 7 10


Через пару секунд появится ответ.

Иными словами мы попросили выдать нам информацию о рейсах, которые вылетят 7 декабря 2014 года после 10 часов из аэропорта Шереметьево. Да, SVO — Шереметьево. А еще UUEE — тоже Шереметьево. Помните, чуть выше я говорил о сокращениях? Вот, мы наткнулись на первое.

Коды аэропортов. IATA vs. ICAO


IATA — Международная ассоциация воздушного транспорта, ИАТА (англ. International Air Transport Association) международная неправительственная организация. Ассоциация выступает координатором и представителем интересов авиатранспортной отрасли в таких областях как обеспечение безопасности полетов, производство полетов, тарифная политика, техобслуживание, авиационная безопасность, разработка международных стандартов совместно с ИКАО и т. д.

ICAO — Международная организация гражданской авиации (International Civil Aviation Organization) — специализированное учреждение ООН, устанавливающее международные нормы гражданской авиации и координирующее её развитие с целью повышения безопасности и эффективности.

И у ИАТА и у ИКАО есть свои коды аэропортов. Они различны, поскольку коды ИАТА выбираются созвучными с названием аэропорта, а код ИКАО основан на том, где находится аэропорт. Именно поэтому у Шереметево код ИАТА SVO, а ИКАО — UUEE, для Пулково, например, LED и ULLI соответственно. Исключение составляют лишь аэропорты США (добавляется «K» к коду ИАТА: Лос-Анджелес — LAX — KLAX) и Канады (добавляется «С»: Торонто — YYZ — CYYZ).

Ответ flightstats


При данном запросе ответ имеет следующую структуру:
{
  посланный запрос
}
"appendix":
{
  "airlines": {...}
  "airports": {...}
  "equipments": {...}
  "flightStatuses": {...}
}

Секции airlines, airports и equipments содержат в себе описание авиакомпаний, аэропортов и типов самолетов, которые присутствуют в секции flightStatuses.
Секция «airlines» предельно проста:
"airlines": [
   {
    "fs": "SU",
    "iata": "SU",
    "icao": "AFL",
    "name": "Aeroflot",
    "active": true
   },
...

Поле «fs» содержит в себе код авиакомпании в базе flightStats. Почти всегда он совпадает с кодом IATA.

Секция «airports» посложнее:
"airports": [
   {
    "fs": "BUD",
    "iata": "BUD",
    "icao": "LHBP",
    "name": "Liszt Ferenc International Airport",
    "city": "Budapest",
    "cityCode": "BUD",
    "countryCode": "HU",
    "countryName": "Hungary",
    "regionName": "Europe",
    "timeZoneRegionName": "Europe/Budapest",
    "localTime": "2013-12-06T20:51:56.974",
    "utcOffsetHours": 1,
    "latitude": 47.433037,
    "longitude": 19.261621,
    "elevationFeet": 495,
    "classification": 2,
    "active": true,
    "delayIndexUrl": "https://api.flightstats.com/flex/delayindex/rest/v1/json/airports/BUD?codeType=fs",
    "weatherUrl": "https://api.flightstats.com/flex/weather/rest/v1/json/all/BUD?codeType=fs"
   }, 
...

Здесь содержится вся необходимая информация, кроме погоды и коэффициента задержки, которые надо запрашивать отдельно.

Секция «equipments».
"equipments": [
   {
    "iata": "319",
    "name": "Airbus Industrie A319",
    "turboProp": false,
    "jet": true,
    "widebody": false,
    "regional": false
   },
...

Описывает базовые характеристики самолета.

Отвлечемся вновь от API.

Учимся различать типы самолетов


Это совсем не сложно. Я подготовил небольшую схему (кликабельна), которая поможет легко сориентироваться в мире летающих машин. Плюс дополнительный гид от Rascko

И теперь подтвержение моих слов:
Airbus A380 vs. Boeing 747


Ил-96 vs. Airbus A340


Boeing 737 vs. Airbus A320


Boeing 757 vs. Boeing 767


Airbus A330 vs. Boeing 777


Як-42 vs. Ту-154 vs. McDonnel Douglas MD-11



Разбираем flightStatus


Содержимое flightStatus. Длинное, поэтому скрыто
{
   "flightId": 317846653,
   "carrierFsCode": "SU",
   "flightNumber": "2030",
   "departureAirportFsCode": "SVO",
   "arrivalAirportFsCode": "BUD",
   "departureDate": {
    "dateLocal": "2013-12-07T10:50:00.000",
    "dateUtc": "2013-12-07T06:50:00.000Z"
   },
   "arrivalDate": {
    "dateLocal": "2013-12-07T10:35:00.000",
    "dateUtc": "2013-12-07T09:35:00.000Z"
   },
   "status": "L",
   "schedule": {
    "flightType": "J",
    "serviceClasses": "RJY",
    "restrictions": ""
   },
   "operationalTimes": {
    "publishedDeparture": {
     "dateLocal": "2013-12-07T10:50:00.000",
     "dateUtc": "2013-12-07T06:50:00.000Z"
    },
    "publishedArrival": {
     "dateLocal": "2013-12-07T10:35:00.000",
     "dateUtc": "2013-12-07T09:35:00.000Z"
    },
    "scheduledGateDeparture": {
     "dateLocal": "2013-12-07T10:50:00.000",
     "dateUtc": "2013-12-07T06:50:00.000Z"
    },
    "estimatedGateDeparture": {
     "dateLocal": "2013-12-07T10:50:00.000",
     "dateUtc": "2013-12-07T06:50:00.000Z"
    },
    "actualGateDeparture": {
     "dateLocal": "2013-12-07T11:27:00.000",
     "dateUtc": "2013-12-07T07:27:00.000Z"
    },
    "scheduledGateArrival": {
     "dateLocal": "2013-12-07T10:35:00.000",
     "dateUtc": "2013-12-07T09:35:00.000Z"
    },
    "estimatedGateArrival": {
     "dateLocal": "2013-12-07T11:12:00.000",
     "dateUtc": "2013-12-07T10:12:00.000Z"
    },
    "actualGateArrival": {
     "dateLocal": "2013-12-07T10:43:00.000",
     "dateUtc": "2013-12-07T09:43:00.000Z"
    }
   },
   "delays": {
    "departureGateDelayMinutes": 37,
    "arrivalGateDelayMinutes": 8
   },
   "flightDurations": {
    "scheduledBlockMinutes": 165,
    "blockMinutes": 136
   },
   "airportResources": {
    "departureTerminal": "D",
    "departureGate": "28",
    "arrivalTerminal": "2"
   },
   "flightEquipment": {
    "scheduledEquipmentIataCode": "320",
    "actualEquipmentIataCode": "A320",
    "tailNumber": "VP-BWI"
   }
  },
...


Назначение полей в большинстве случаев очевидно. Я подробно расскажу лишь о тех, содержимое которых не совсем очевидно. А почему? Правильно, потому что сокращения.
Вот эта часть ответа:
"status": "L",
   "schedule": {
    "flightType": "J",
    "serviceClasses": "RJY",
    "restrictions": ""
   },

Поле Описание
status Текущий статус рейса
A — Active
C — Canceled
D — Diverted — Была произведена смена пункта назначения (например, по метео-условиям)
DN — Data source needed — Неоткуда получить информацию о статусе
L — Landed
NO — Not Operational
R — Redirected
S — Scheduled
U — Unknown
flightType Тип рейса. Всего их существует 23 штуки. Например,
J — Scheduled Passanger — Пассажириский по расписанию
M — Scheduled Cargo/Mail(MailOnly) — Грузовой, но только с письмами.
W — Military — Военный
serviceClasses Варианты сервиса, предусмотренные на рейсе по классификации IATA. Подробнее тут — http://en.wikipedia.org/wiki/IATA_class_codes
restrictions Ограничения по классификации IATA. Подробнее — http://www.flyerguide.com/wiki/index.php/Traffic_Restriction_Codes_(AA)


Программирование


На данный момент я использую python 2.7, urllib2 и simplejson.

Первое, что нужно сделать — подключить необходимые библиотеки и проинициализировать переменные.
import urllib2
import simplejson

appId = "Ваш appId тут"
appKey = "Ваш appKey тут"

# Название аэропорта. Может быть запрошен как по внутреннему коду flightstats, так и по кодам ICAO или IATA
requestedAirport = "SVO"

# Какие рейсы нам нужны. arr - прибывающие, dep - отбывающие
flightsType = "arr"

# Дата 
requestedDate = "2013/12/7"

# Время, с которого мы хотим получить список рейсов
requestedHour = "15"

# Количество часов, за которые будет составлен список
requestedNumHours = "6"


Следующий шаг — упаковываем эти переменные в url, отправляем запрос и ждем ответа.
# Заготовка для запроса
url = "https://api.flightstats.com/flex/flightstatus/rest/v2/json/" \
       "airport/status/%s/%s/%s/%s?appId=%s&appKey=%s&utc=false&numHours=%s" 

# Подставляем нужные значения в запрос        
url = url %(requestedAirport, flightsType, requestedDate, requestedHour, appId, appKey, requestedNumHours)

# Шлем запрос и получаем JSON-ответ            
req = urllib2.Request(url, None)
opener = urllib2.build_opener()
f = opener.open(req)         
response = simplejson.load(f)


Затем парсим вспомогательные поля. Они нам нужны для того, чтобы подставлять развернутые названия самолетов и аэропотов в список.
# Сохраняем ветку с аэропортами
airports = response["appendix"]["airports"]

# Данные по аэропортам будут храниться в словаре (dictionary)
airportsDict = dict()

# Для каждого аэропорта записываем пару [код flightstats]:[название]
for airport in airports:
    airportsDict[airport["fs"]] = airport["name"]

# Аналогично поступаем для типов бортов...
equipments = response["appendix"]["equipments"]
equipmentsDict = dict()
for equipment in equipments:
    equipmentsDict[equipment["iata"]] = equipment["name"], equipment["iata"] 

#... и для авиакомпаний
airlines = response["appendix"]["airlines"]
airlinesDict = dict()
for airline in airlines:
    airlinesDict[airline["fs"]] = airline["name"]


Результатом работы нашего кода должна быть вот такая таблица:
Flight Carrier Equipment Registration From STD ATD To STA STD
XQ114 SunExpress Boeing 737-800 Passenger D-ASXA Antalya 15:00:00.000 --- CGN 17:55:00.000 ---


Выводить данные будем в HTML.
# Заготовка для страницы
webPage = "<html><body><table border=\"1\">    \
           <tr><th>Flight</th><th>Carrier</th><th>Equipment</th><th>Registration</th><th>From</th><th>STD</th>   \
           <th>ATD</th><th>To</th><th>STA</th><th>ETA</th></tr>"

# Заготовка для строки таблицы
templateRow = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td> \
               <td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>"

f = open("./list.html", "w")


Далее необходимо написать вспомогательную функцию.
Нужные нам значения лежат на разной глубине. Например, carrierFsCode, код аэропорта, на нулевой глубине. А чтобы добыть время фактическое время вылета, нужно опуститься на вторую глубину: operationalTimes --> actualGateDeparture --> dateLocal". Для этого нужна первая вспомогательная функция.
def getProperty(status, propertyNames):
  # Пытаемся найти нужный ключ
  try:
      # Перебираем каждый ключ из propertyNames
      for propertyName in propertyNames:
          # Отсекаем ненужное  
          status = status[propertyName]
      
      # Нужный ключ найден!
      return status
   except
      # А если нет, то возвращаем заглушку
      return "---"


Теперь самое интересное: необходимо выбрать то, что вас наиболее интересует в трафике. Это содержится в трех массивах:
interestingCarriers = ["RU", # AirBridgeCargo
                       "CU", # Cubana de Aviacion
                       "ME", # China Eastern Airlines
                       "KE", # Korean Air Lines
                       ]
interestingEquipments = ["SU9"] # Sukhoi Superjet 100

interestingTailNumbers = ["VP-BGB"] # Номер первого Boeing 777-300ER для Аэрофлота


А теперь, собственно, парсер:
for flightStatus in flightStatuses:
    newRow = templateRow %(getProperty(flightStatus, ["carrierFsCode"]) + getProperty(flightStatus, ["flightNumber"]),
                   airlinesDict[getProperty(flightStatus, ["carrierFsCode"])],
                   getProperty(flightStatus, ["flightEquipment", "scheduledEquipmentIataCode"]),
                   getProperty(flightStatus, ["flightEquipment", "tailNumber"]),
                   str(airportsDict[getProperty(flightStatus, ["departureAirportFsCode"])]).replace("Airport", ""),
                   str(getProperty(flightStatus, ["departureDate", "dateLocal"])).split("T")[-1],
                   str(getProperty(flightStatus, ["operationalTimes", "actualGateDeparture", "dateLocal"])).split("T")[-1],
                   str(airportsDict[getProperty(flightStatus, ["arrivalAirportFsCode"])]).replace("Airport", "")
                   str(getProperty(flightStatus, ["arrivalDate", "dateLocal"])).split("T")[-1],
                   str(getProperty(flightStatus, ["operationalTimes", "estimatedGateArrival", "dateLocal"])).split("T")[-1])

    # Подсвечиваем необходимую строку
    if (getProperty(flightStatus, ["carrierFsCode"]) in interestingCarriers) or \
        (getProperty(flightStatus, ["flightEquipment", "scheduledEquipmentIataCode"]) in interestingEquipments) or \
        (getProperty(flightStatus, ["flightEquipment", "tailNumber"]) in interestingTailNumbers):
        newRow = newRow.replace("<tr>", "<tr bgcolor=\"#FF0000\">")
    
    #  Добавляем ее к странице
    webPage += newRow


Завершающий штрих — дописываем теги в конец страницы и закрываем файл.
webPage = webPage + "</table></body></html>"
f.write(webPage)
f.close()

Результат работы

Flight Carrier Equipment Registration From STD ATD To STA ETA
SU155 Aeroflot 332 VQ-BBE Cancun International 12:30:00.000 13:17:00.000 Sheremetyevo International 10:30:00.000 11:03:00.000
DL466 Delta Air Lines 76W --- John F. Kennedy International 16:15:00.000 16:14:00.000 Sheremetyevo International 10:50:00.000 10:12:00.000
SU111 Aeroflot 332 VP-BLX Miami International 17:35:00.000 18:35:00.000 Sheremetyevo International 13:45:00.000 13:46:00.000
SU103 Aeroflot 333 VP-BDE John F. Kennedy International 19:05:00.000 --- Sheremetyevo International 13:25:00.000 13:34:00.000
UN576 Transaero Airlines 744 EI-XLJ Punta Cana International 19:55:00.000 21:18:00.000 Sheremetyevo International 14:50:00.000 15:35:00.000
RU566 AirBridgeCargo 74Y --- Frankfurt am Main 04:45:00.000 --- Sheremetyevo International 11:00:00.000 ---
RU498 AirBridgeCargo 74N --- Shanghai Pudong International 05:00:00.000 --- Sheremetyevo International 10:45:00.000 ---
SU233 Aeroflot 332 --- Indira Gandhi International 05:05:00.000 05:26:00.000 Sheremetyevo International 10:10:00.000 10:13:00.000
RU506 AirBridgeCargo 74N --- Milano Malpensa 05:30:00.000 --- Sheremetyevo International 12:00:00.000 ---
SU1827 Aeroflot 320 VQ-BAZ Simferopol 06:00:00.000 06:25:00.000 Sheremetyevo International 10:15:00.000 10:40:00.000
SU2437 Aeroflot 320 VP-BLH Dusseldorf International 06:05:00.000 06:27:00.000 Sheremetyevo International 12:25:00.000 12:24:00.000
RU440 AirBridgeCargo 74N VP-BIM Hong Kong International 06:15:00.000 06:15:00.000 Sheremetyevo International 12:25:00.000 ---
KE529 Korean Air Lines 74Y HL7466 Incheon International 06:25:00.000 07:07:00.000 Sheremetyevo International 10:40:00.000 ---
JU650 Jat Airways 733 --- Belgrad Nikola Tesla 06:45:00.000 06:45:00.000 Sheremetyevo International 12:35:00.000 12:39:00.000
PS561 UIA 73N UR-GAP Kiev/Kyiv — Borispol 07:00:00.000 07:00:00.000 Sheremetyevo International 10:35:00.000 10:35:00.000
SU1009 Aeroflot 321 VQ-BED Kaliningrad 07:10:00.000 07:36:00.000 Sheremetyevo International 10:00:00.000 10:26:00.000
AF1644 Air France 319 F-GRHL Charles de Gaulle 07:15:00.000 07:13:00.000 Sheremetyevo International 13:55:00.000 13:52:00.000
SU1867 Aeroflot 320 VP-BQP Zvartnots International 08:10:00.000 08:21:00.000 Sheremetyevo International 11:00:00.000 11:11:00.000
5N502 Nordavia Regional Airlines 735 --- Syktyvkar 08:20:00.000 08:27:00.000 Sheremetyevo International 10:15:00.000 10:11:00.000
KC893 Air Astana 320 P4-KBC Astana 08:40:00.000 08:36:00.000 Sheremetyevo International 10:20:00.000 10:49:00.000
SU3 Aeroflot 321 VP-BWO Pulkovo 08:55:00.000 09:04:00.000 Sheremetyevo International 10:20:00.000 10:29:00.000
SU1513 Aeroflot 319 VP-BWA Surgut 09:00:00.000 08:59:00.000 Sheremetyevo International 10:35:00.000 10:34:00.000
SU1293 Aeroflot 320 VQ-BIV Kazan 09:00:00.000 09:27:00.000 Sheremetyevo International 10:30:00.000 10:50:00.000
SU1229 Aeroflot 320 VP-BDK Nizhniy Novgorod 09:05:00.000 09:21:00.000 Sheremetyevo International 10:25:00.000 10:41:00.000
SU1309 Aeroflot 319 VP-BDO Samara 09:15:00.000 09:20:00.000 Sheremetyevo International 10:55:00.000 11:00:00.000
AY153 Finnair 319 OH-LVI Helsinki-Vantaa 09:25:00.000 09:29:00.000 Sheremetyevo International 13:05:00.000 12:57:00.000
OK892 CSA 319 --- Vaclav Havel Prague 09:30:00.000 09:31:00.000 Sheremetyevo International 15:10:00.000 15:05:00.000
SU2005 Aeroflot 320 VP-BWI J. Paul II International Krakow-Balice 09:35:00.000 09:56:00.000 Sheremetyevo International 14:40:00.000 14:49:00.000
SU1121 Aeroflot 320 VP-BTI Adler/Sochi 09:50:00.000 09:55:00.000 Sheremetyevo International 12:20:00.000 12:25:00.000
SU2685 Aeroflot 320 VQ-BCM Schoenefeld 09:50:00.000 10:44:00.000 Sheremetyevo International 15:25:00.000 16:15:00.000
SU5 Aeroflot 320 VQ-BAX Pulkovo 09:55:00.000 10:20:00.000 Sheremetyevo International 11:15:00.000 11:40:00.000
SU1839 Aeroflot SU9 RA-89010 Kharkov 09:55:00.000 10:10:00.000 Sheremetyevo International 13:30:00.000 13:20:00.000
SU2321 Aeroflot 320 VQ-BHL Franz Josef Strauss 10:00:00.000 10:16:00.000 Sheremetyevo International 16:00:00.000 16:16:00.000
SU1001 Aeroflot 320 VP-BLL Kaliningrad 10:05:00.000 10:25:00.000 Sheremetyevo International 12:55:00.000 13:15:00.000
R25807 Orenair 738 --- Barnaul 10:10:00.000 10:15:00.000 Sheremetyevo International 11:30:00.000 11:35:00.000
SU1307 Aeroflot 320 VP-BKX Tolmachevo 10:15:00.000 10:19:00.000 Sheremetyevo International 11:25:00.000 11:29:00.000
SU1701 Aeroflot 333 VQ-BNS Vladivostok International 10:20:00.000 10:24:00.000 Sheremetyevo International 12:25:00.000 12:29:00.000
SU1805 Aeroflot 321 VP-BOE Kiev/Kyiv — Borispol 10:20:00.000 11:00:00.000 Sheremetyevo International 13:50:00.000 14:30:00.000
SU2137 Aeroflot 321 VQ-BHK Istanbul Ataturk 10:20:00.000 11:03:00.000 Sheremetyevo International 15:15:00.000 15:26:00.000
SK734 SAS 320 OY-KAP Copenhagen 10:20:00.000 10:46:00.000 Sheremetyevo International 15:45:00.000 16:02:00.000
SU7 Aeroflot 320 --- Pulkovo 10:25:00.000 10:43:00.000 Sheremetyevo International 11:45:00.000 12:03:00.000
SU1813 Aeroflot 320 VP-BRX Donetsk 10:30:00.000 10:31:00.000 Sheremetyevo International 14:25:00.000 14:26:00.000
SU1831 Aeroflot 320 --- Minsk International 2 10:50:00.000 11:40:00.000 Sheremetyevo International 13:15:00.000 14:05:00.000
SU2107 Aeroflot 320 VP-BZS Tallinn 10:50:00.000 10:54:00.000 Sheremetyevo International 14:30:00.000 14:18:00.000
SU1479 Aeroflot 319 VP-BDM Abakan 10:55:00.000 10:55:00.000 Sheremetyevo International 11:55:00.000 11:55:00.000
SU1483 Aeroflot 77W VP-BGB Krasnojarsk 11:00:00.000 11:13:00.000 Sheremetyevo International 11:35:00.000 11:48:00.000
SU2683 Aeroflot 319 VQ-BCO Riga 11:00:00.000 11:24:00.000 Sheremetyevo International 14:35:00.000 14:44:00.000
D95399 Donavia 319 VP-BNN Stavropol 11:15:00.000 11:17:00.000 Sheremetyevo International 13:30:00.000 13:32:00.000
SU2035 Aeroflot SU9 RA-89008 Otopeni International 11:15:00.000 11:28:00.000 Sheremetyevo International 15:35:00.000 15:33:00.000
SU11 Aeroflot 320 --- Pulkovo 11:30:00.000 11:49:00.000 Sheremetyevo International 12:45:00.000 13:04:00.000
SU1139 Aeroflot 321 VQ-BKU Adler/Sochi 11:35:00.000 11:55:00.000 Sheremetyevo International 14:00:00.000 14:20:00.000
SU1211 Aeroflot 320 VQ-BIT Samara 11:40:00.000 12:13:00.000 Sheremetyevo International 13:25:00.000 13:42:00.000
SU1759 Aeroflot SU9 VP-BZQ Volgograd 11:45:00.000 11:53:00.000 Sheremetyevo International 13:35:00.000 13:43:00.000
SU1255 Aeroflot 319 VP-BDN Begishevo 11:50:00.000 12:03:00.000 Sheremetyevo International 13:40:00.000 13:53:00.000
SU1643 Aeroflot 320 VQ-BIW Astrakhan 11:50:00.000 11:55:00.000 Sheremetyevo International 14:10:00.000 14:15:00.000
SU1305 Aeroflot 320 VP-BLP Mineralnye Vody 11:50:00.000 12:08:00.000 Sheremetyevo International 14:15:00.000 14:33:00.000
SU1761 Aeroflot 738 VP-BRH Chita 11:55:00.000 12:10:00.000 Sheremetyevo International 12:45:00.000 13:00:00.000
SU1221 Aeroflot 320 VP-BMF Nizhniy Novgorod 12:05:00.000 12:12:00.000 Sheremetyevo International 13:10:00.000 13:17:00.000
SU1743 Aeroflot 333 VQ-BQX Yuzhno-Sakhalinsk 12:10:00.000 12:20:00.000 Sheremetyevo International 14:05:00.000 14:15:00.000
D95301 Donavia 734 VQ-BCS Rostov 12:15:00.000 12:28:00.000 Sheremetyevo International 14:15:00.000 14:28:00.000
SU13 Aeroflot 319 --- Pulkovo 12:20:00.000 12:50:00.000 Sheremetyevo International 13:35:00.000 14:05:00.000
5N117 Nordavia Regional Airlines 735 --- Arkhangelsk 12:20:00.000 12:25:00.000 Sheremetyevo International 14:05:00.000 14:10:00.000
SU1191 Aeroflot 320 VQ-BEA Kazan 12:25:00.000 13:04:00.000 Sheremetyevo International 13:55:00.000 14:34:00.000
SU1751 Aeroflot 738 VP-BRF Yakutsk 12:30:00.000 12:58:00.000 Sheremetyevo International 13:15:00.000 13:43:00.000
SU1547 Aeroflot SU9 --- Anapa 12:30:00.000 12:50:00.000 Sheremetyevo International 14:45:00.000 15:05:00.000
D95377 Donavia 319 --- Mineralnye Vody 12:45:00.000 13:03:00.000 Sheremetyevo International 15:10:00.000 15:28:00.000
D95363 Donavia 319 VP-BQK Rostov 13:05:00.000 13:20:00.000 Sheremetyevo International 15:05:00.000 15:20:00.000
SU1411 Aeroflot 321 VQ-BOI Koltsovo International 13:15:00.000 13:43:00.000 Sheremetyevo International 13:40:00.000 14:08:00.000
SU1731 Aeroflot 333 VQ-BCQ Petropavlovsk-Kamchatsky 13:30:00.000 13:44:00.000 Sheremetyevo International 14:30:00.000 14:44:00.000
SU15 Aeroflot 320 --- Pulkovo 13:30:00.000 13:39:00.000 Sheremetyevo International 14:45:00.000 14:52:00.000
SU1231 Aeroflot 320 VP-BLR Ufa 13:55:00.000 14:19:00.000 Sheremetyevo International 14:00:00.000 14:24:00.000
SU1421 Aeroflot 320 VP-BNL Chelyabinsk 13:55:00.000 13:56:00.000 Sheremetyevo International 14:20:00.000 14:21:00.000
R25803 Orenair 738 --- Irkutsk 14:05:00.000 14:30:00.000 Sheremetyevo International 14:50:00.000 15:15:00.000
SU1201 Aeroflot SU9 --- Perm 14:10:00.000 --- Sheremetyevo International 14:25:00.000 14:25:00.000
5N9134 Nordavia Regional Airlines --- --- Kazan 14:10:00.000 15:07:00.000 Sheremetyevo International 15:30:00.000 ---
SU17 Aeroflot 320 --- Pulkovo 14:25:00.000 14:56:00.000 Sheremetyevo International 15:40:00.000 16:11:00.000


Future work


  • Хочу сделать более красивый вид таблицы результатами
  • Нормальный вывод в PDF, а не как печать web-страницы
  • Приложение для Android
Метки:
Поделиться публикацией
Похожие публикации
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 34
  • +2
    У меня в свое время выработались, дополнительно к указанным Вами, следующие критерии различения самолетов:

    1) Boeing vs Airbus: у Airbus встречается характерная законцовка крыла в форме «стрелочки». Если видишь ее — то перед тобой точно Airbus.

    2) Boeing-737: имеет характерно приплюснутые снизу воздухозаборники двигателей, в отличие от Airbus, у которого воздухозаборники всегда круглые.

    3) Boeing-777: самая задняя точка фюзеляжа заострена, как бы заточена с двух сторон по горизонтали. Выхлоп APU идет слегка вбок. На остальных самолетах в задней точке размещается выхлоп APU, и поэтому «законцовка» не такая острая.

    4) При размещении двигателей не под крыльями, а в хвостовой части самолета. Тут тоже есть несколько критериев различения. Например, Canadair Regional Jet — маленький, с двигателями в хвосте. Ну а еще такую компоновку имеет Ту-154. Разве они уже не летают из Шереметьево?
    • 0
      имеет характерно приплюснутые снизу воздухозаборники двигателей
      Начиная с NG (600,700, 800, 900) это уже намного менее заметно — например, вот:

      image
      • 0
        1) Винглеты:
        image
    • +1
      def getProperty(status, propertyNames):
        # Cохраняем все содержимое status
        property = status
      
        # Пытаемся найти нужный ключ
        try:
            # Перебираем каждый ключ из propertyNames
            for propertyName in propertyNames:
                # Отсекаем ненужное  
                property = property[propertyName]
            
            # Нужный ключ найден!
            return property
         except
            # А если нет, то возвращаем заглушку
            return "---"
      


      1) Except всегда должен быть не глобальным, а подходящим к одной конкретной ошибке, дабы избежать скрытых ошибок, которые вы не ожидали. В Вашем случае должен быть except KeyError: Shit реально happens.
      2)
      # Cохраняем все содержимое status
        property = status
      

      А зачем, собственно? Локальная переменная status не изменится за пределами функции, даже если в функции вы поимеете поизменяете её во все дыры методы.
      3) И так, для себя — можно объяснить значение этой функции? Что она реально делает? Почему нельзя было написать:
      try:
          dateLocal = data['operationalTimes']['actualGateDeparture']['dateLocal']
      except:
          dateLocal = '---'
      

      или что-то в этом роде?
      • 0
        1, 2 — поправил, спасибо.
        3 — можно, не спорю. Но дело в том, что ключ из ответа сразу подставляется в заготовленную строку. Если бы я сохранял значение в переменную, тогда да, именно так бы и написал.
      • +1
        Простите, а отечественные типы самолетов вообще не рассматриваются?
        Такой футбол нам обиден.
        • +1
          Ну почему не рассматриваю? Их просто нет в классификации.
          Равно как и самолетов марки Embraer, ATR, Bombardier. И не потому что я к ним плохо отношусь, просто они редко встречаются.
          Однако, как Вы можете заметить, в коде есть interestingEquipments = ["SU9"] # Sukhoi Superjet 100.

          А вообще, если кода не касаться, то лично я, когда выезжаю фотографировать, считаю, что выезд удался, если я смог сфотографировать хотя бы один отечественный самолет.
          • 0
            «Учимся различать типы самолетов»: ни одного отечественного типа на схеме. С учетом Вашего комментария выше, схема не удалась.
            Нужно обязательно добавить сюда Тушки, Илы, Яки и т.д., пока отечественная авиация еще летает.
            • +1
              Добавил Ил-95, Як-42, Ту-154
              • +4
                Вспомнил, что когда-то рисовал похожий гайд, вот кусок из него, касающийся некоторых неупомянутых Вами джетов (в основном, советских/постсоветских).



                Кстати, вспомнил же, что тогда не сумел придумать отличий между 204 и 757 без «очень хорошей фотки» и не опираясь на ливреи.
                • 0
                  Добавил в пост!
                • 0
                  Спасибо. Так справедливее.
                  Из Шарика SSJ еще часто должны летать, по крайней мере, значительно чаще чем из других московских аэропортов.
                  • 0
                    Именно поэтому он в коде выделен. Та конечная таблица — как раз для Шарика
                  • 0
                    Нет в природе пассажирской ИЛ-95.
            • +1
              По поводу «схемки». Я бы вот что заметил: 737 и 32х проще всего различать по килю: у 737 перед основным килем своеобразный «гаргрот», которого нет ни на одном 32х. Насчет 757 и 767 — 757 визуально меньше, его с убранным шасси легче спутать с 321, а 767 как раз легче спутать с 330.
              • 0
                Давно была идея следить за расписанием авиарейса и подписываться на изменения в нем (по смс или звонком).
                Местный аэропорт не всегда считает нужным уведомлять об отмене/переносе рейса.
                Насколько точные данные выдает flightstats.com?
                • 0
                  Хм, я точность работы не проверял, но я думаю, что вполне точные. По крайней мере для задач споттинга.
                • –14
                  Кроме говнокода, хотелось бы отметить такие замечательные критерии оценки самолетов, как «острый нос и толстый фюзеляж». Вы сами-то попробуете отличить самолеты по такому критерию без сравнения, когда видете один самолет в небе?

                  P.S. И причем здесь DIY… Хотя с другой стороны, по уровню дилетанства в самый раз и туда тоже :)
                  • +2
                    И именно так я их и отличаю.
                  • 0
                    Ой спасибо, как раз думал как бы а380 на взлете словить.
                  • 0
                    Делал интеграцию с Flighstats, вполне себе вменяемый API, правда иногда отвечает не быстро.
                    Кстати, вот цены на их API:
                    developer.flightstats.com/getting-started/pricing
                    • 0
                      а погода там точная, от каждого аэропорта?
                      можете для пулково один запрос по погоде сделать, если не трудно?)
                      • +1
                        Иногда, конечно, бывают задержки.
                        Погода в Пулково
                        {
                         "request": {
                          "url": "https://api.flightstats.com/flex/weather/rest/v1/json/metar/ULLI",
                          "airport": {
                           "requestedCode": "ULLI",
                           "fsCode": "LED"
                          },
                          "codeType": {}
                         },
                         "metar": {
                          "report": "ULLI 081130Z 00000MPS 9999 -SN SCT010 OVC018 M03/M04 Q1008 60590336 10////// NOSIG",
                          "reportTime": "2013-12-08T11:30:00.000Z",
                          "weatherStationIcao": "ULLI",
                          "tags": [
                           {
                            "key": "Precipitation",
                            "value": "2"
                           },
                           {
                            "key": "Freezing",
                            "value": "4"
                           },
                           {
                            "key": "Instrumentation",
                            "value": "MVFR"
                           },
                           {
                            "key": "Prevailing Conditions",
                            "value": "Snow"
                           }
                          ],
                          "conditions": {
                           "wind": {
                            "direction": 0,
                            "directionIsVariable": false,
                            "speedKnots": "0.00"
                           },
                           "visibility": {
                            "miles": "6.21",
                            "lessThan": false,
                            "cavok": false
                           },
                           "weatherConditions": [
                            {
                             "phenomenon": "Snow",
                             "intensity": "Light"
                            }
                           ],
                           "skyConditions": [
                            {
                             "coverage": "Scattered clouds",
                             "base": 1000
                            },
                            {
                             "coverage": "Overcast",
                             "base": 1800
                            }
                           ],
                           "pressureInchesHg": "29.77"
                          },
                          "temperatureCelsius": "-3.00",
                          "dewPointCelsius": "-4.00",
                          "runwayVisualRanges": [],
                          "obscurations": [],
                          "noSignificantChange": true
                         },
                         "appendix": {
                          "airports": [
                           {
                            "fs": "LED",
                            "iata": "LED",
                            "icao": "ULLI",
                            "name": "Pulkovo Airport",
                            "city": "Saint Petersburg",
                            "cityCode": "LED",
                            "countryCode": "RU",
                            "countryName": "Russian Federation",
                            "regionName": "Europe",
                            "timeZoneRegionName": "Europe/Moscow",
                            "localTime": "2013-12-08T16:14:21.111",
                            "utcOffsetHours": 4,
                            "latitude": 59.806085,
                            "longitude": 30.3083,
                            "elevationFeet": 79,
                            "classification": 2,
                            "active": true,
                            "delayIndexUrl": "https://api.flightstats.com/flex/delayindex/rest/v1/json/airports/LED?codeType=fs"
                           }
                          ]
                         }
                        }
                        • 0
                          Спасибо, вроде похоже на правду за окном :)
                      • +2
                        russianplanes.net/rasp/?place=SVO&date=2013.12.09
                        сделано шесть лет назад
                        • 0
                          Не знал об этом, спасибо!
                        • 0
                          Спасибо за обзор этого API! Как-то раньше не догадывался, что есть такая хорошая и открытая возможность получать данные по авиарейсам. Теперь можно отключить древнюю парсилку табло аэропортов.
                          P.S. жить в 20 минутах от шереметьево — это жить в самом шереметьево. Ну или вести глубокую ночную жизнь ;)
                          • +1
                            Если приобрести SDR приёмник то на частоте 1090 МГц можно принимать сигналы прилетающих самолётов.
                            • +1
                              Думаете, я этого не делал? :)

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.