National Weather Service (NWS) API를 사용하는 방법 및 Python 예제 코드

Posted by

National Weather Service(NWS) API는 미국 국립기상청에서 제공하는 무료 공공 데이터 API로, 다양한 날씨 정보를 제공하며, 특히 별도 API 키 없이 사용 가능하므로, 손쉽게 사용할 수 있다. 이번 포스트에서는 Python 기반으로 National Weather Service(NWS) API를 사용하는 방법과 리턴되는 결과를 같이 살펴보도록 하자.

National Weather Service(NWS) API 소개

NWS API는 다양한 날씨 데이터를 제공하는 여러 엔드포인트(Endpoint)를 가지고 있다.

엔드포인트(Endpoint)란?

API 엔드포인트는 API가 서버의 특정 리소스에 대한 요청을 받는 디지털 위치를 말한다.
API에서 엔드포인트는 일반적으로 서버의 리소스 위치를 제공하는 URL(Uniform Resource Locator)을 뜻한다.

각 엔드포인트는 특정한 유형의 날씨 정보에 접근할 수 있도록 설계되어 있다.

  • /points: 위치 기반 날씨 데이터
  • /gridpoints: 그리드 기반 날씨 데이터
  • /alerts: 날씨 경고 및 알림
  • /stations: 기상 관측소 데이터

좀 더 자세한 엔드포인트 종류에 대한 설명은 포스트 마지막에 다시 설명하도록 하겠다.

API 키 발급

NWS API는 무료로 사용할 수 있으며, 별도의 API 키가 필요 없어, 손쉽게 사용 가능하다.

API 요청 및 응답 형식

NWS API는 HTTP 요청을 통해 데이터를 제공한다. 응답은 주로 JSON 형식으로 반환된다.


기본 요청 예제

위치 기반 현재 날씨 데이터 가져오기

/point NWS API는 위치(위도와 경도 혹은 주)를 기반으로 동작한다.
여기서는 미국 텍사스 댈러스(위도: 32.7767 N, 경도: 96.7970 W)의 현재 날씨 데이터를 가져오는 예제를 살펴 보도록 하자.

먼저 엔드포인트 뒤에 위도,경도를 입력하고 requests.get을 통해 데이터를 요청하고 반환 받는다.
데이터는 JSON 형태로 반환되며, 이 데이터에는 메타 데이터, 식별자, 지리적 좌표, 날씨 정보와 관련된 링크가 포함되어 있다.
다음으로 관측소에 대한 링크 주소를 파싱하고, 그 링크 주소를 통해 현재 날씨 정보를 다시 요청하고, 반환 받아 현재 날씨 정보를 파싱, 정리하여 출력하게 된다.

import requests

# 댈러스의 위도와 경도
latitude = 32.7767
longitude = -96.7970

# NWS API 엔드포인트
points_url = f"https://api.weather.gov/points/{latitude},{longitude}"

# 위치 정보 가져오기
response = requests.get(points_url)
data = response.json()

# 관측소 URL 추출
observation_stations_url = data['properties']['observationStations']

# 관측소 데이터 가져오기
stations_response = requests.get(observation_stations_url)
stations_data = stations_response.json()
stations = stations_data['observationStations']

# 첫 번째 관측소 데이터 가져오기
station_url = stations[0]

# 현재 날씨 데이터 가져오기
current_weather_url = f"{station_url}/observations/latest"
current_weather_response = requests.get(current_weather_url)
current_weather_data = current_weather_response.json()

# 현재 날씨 정보 출력
current_observation = current_weather_data['properties']

print("현재 날씨")
print(f"온도: {current_observation['temperature']['value']}°C")
print(f"습도: {current_observation['relativeHumidity']['value']}%")
print(f"바람 속도: {current_observation['windSpeed']['value']} m/s")
print(f"풍향: {current_observation['windDirection']['value']}°")
print(f"날씨 상태: {current_observation['textDescription']}")

결과 출력

현재 날씨
온도: 32.8°C
습도: 63.641629175669%
바람 속도: 20.52 m/s
풍향: 160°
날씨 상태: Mostly Cloudy

날씨 경고 및 알림 가져오기

날씨 경고 및 알림 예제는 먼저 /point 엔드포인트로 경고 및 알림 URL을 받아 온 뒤, /alerts 엔드포인트로 경고 및 알림 데이터를 받아와 정보를 정리하여 출력한다.

import requests

# 오클라호마 시티의 위도와 경도
latitude = 35.4676
longitude = -97.5164

# NWS API 엔드포인트
points_url = f"https://api.weather.gov/points/{latitude},{longitude}"

# 위치 정보 가져오기
response = requests.get(points_url)
data = response.json()

# 경고 및 알림 URL 추출
forecast_zone_url = data['properties']['forecastZone']

# 경고 및 알림 데이터 가져오기
alerts_url = f"https://api.weather.gov/alerts/active?zone={forecast_zone_url.split('/')[-1]}"
alerts_response = requests.get(alerts_url)
alerts_data = alerts_response.json()

# 경고 및 알림 정보 출력
alerts = alerts_data['features']

if alerts:
    print("현재 날씨 경고 및 알림")
    for alert in alerts:
        properties = alert['properties']
        print(f"제목: {properties['headline']}")
        print(f"상태: {properties['event']}")
        print(f"설명: {properties['description']}")
        print(f"지시사항: {properties['instruction']}")
        print("-" * 40)
else:
    print("현재 활성화된 날씨 경고 및 알림이 없습니다.")

결과 출력

현재 날씨 경고 및 알림
제목: Flood Watch issued June 4 at 1:34PM CDT until June 5 at 7:00AM CDT by NWS Norman OK
상태: Flood Watch
설명: * WHAT...Flooding caused by excessive rainfall is possible.

* WHERE...Portions of central, east central, southeast, and southern
Oklahoma, including the following counties, in central Oklahoma,
Cleveland, Grady, Lincoln, McClain, Oklahoma and Pottawatomie. In
east central Oklahoma, Pontotoc and Seminole. In southeast
Oklahoma, Atoka, Bryan, Coal, Hughes, Johnston and Marshall. In
southern Oklahoma, Garvin and Murray.

* WHEN...From 7 PM CDT this evening through Wednesday morning.

* IMPACTS...Excessive runoff may result in flooding of rivers,
creeks, streams, and other low-lying and flood-prone locations.

* ADDITIONAL DETAILS...
- A complex of storms is expected to bring heavy rain to areas
already saturated from previous rains.
- http://www.weather.gov/safety/flood
지시사항: You should monitor later forecasts and be alert for possible Flood
Warnings. Those living in areas prone to flooding should be prepared
to take action should flooding develop.
----------------------------------------

엔드포인트에 대한 자세한 설명

/points

특정 위치(위도 및 경도)의 날씨 정보를 제공한다.

  • 예시: https://api.weather.gov/points/{latitude},{longitude}
  • 반환 데이터: 예보 및 그리드 포인트 URL, 관측소 정보 등

/gridpoints

특정 그리드 포인트의 상세 날씨 정보를 제공한다.

  • 예시: https://api.weather.gov/gridpoints/{wfo}/{x},{y}
  • 반환 데이터: 특정 그리드 포인트의 날씨 예보 데이터

/alerts

특정 지역의 날씨 경고 및 알림을 제공한다.

  • 예시: https://api.weather.gov/alerts/active?area={state}
  • 반환 데이터: 활성화된 날씨 경고 및 알림

/stations

기상 관측소의 데이터를 제공한다.

  • 예시: https://api.weather.gov/stations/{stationId}/observations/latest
  • 반환 데이터: 특정 관측소의 최신 관측 데이터

고급 사용 예시

위치 기반 7일 예보 데이터 시각화

댈러스(위도: 32.7767 N, 경도: 96.7970 W)의 7일 예보 데이터를 가져와 시각화하는 예시다.
아래 코드를 활용하여 더 보기 좋은 시각화 결과를 만들어 보자.

import requests
import pandas as pd
import matplotlib.pyplot as plt

# 댈러스의 위도와 경도
latitude = 32.7767
longitude = -96.7970

# NWS API 엔드포인트
points_url = f"https://api.weather.gov/points/{latitude},{longitude}"
response = requests.get(points_url)
data = response.json()

# 예보 URL 추출
forecast_url = data['properties']['forecast']

# 예보 데이터 가져오기
forecast_response = requests.get(forecast_url)
forecast_data = forecast_response.json()

# 날짜별 최고/최저 온도 데이터 추출
periods = forecast_data['properties']['periods']
dates = [period['startTime'][:10] for period in periods if period['isDaytime']]
highs = [period['temperature'] for period in periods if period['isDaytime']]
lows = [period['temperature'] for period in periods if not period['isDaytime']]

# 화씨에서 섭씨로 변환
highs_c = [(temp - 32) * 5/9 for temp in highs]
lows_c = [(temp - 32) * 5/9 for temp in lows]

# 데이터프레임 생성
df = pd.DataFrame({
    'Date': dates[:len(lows_c)],
    'High (°C)': highs_c[:len(lows_c)],
    'Low (°C)': lows_c
})

# 데이터 시각화
plt.figure(figsize=(10, 5))
plt.plot(df['Date'], df['High (°C)'], label='High Temp (°C)', marker='o')
plt.plot(df['Date'], df['Low (°C)'], label='Low Temp (°C)', marker='o')
plt.fill_between(df['Date'], df['High (°C)'], df['Low (°C)'], color='grey', alpha=0.2)
plt.xlabel('Date')
plt.ylabel('Temperature (°C)')
plt.title('7-Day Temperature Forecast for Dallas, TX')
plt.xticks(rotation=45)
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

결과 출력

오류 처리 및 예외 처리

NWS API를 사용할 때에는 언제든지 다양한 오류가 발생할 수 있으니, 발생할 수 있는 오류를 예외 처리 하는 방법도 같이 알아보자.

import requests
import json

# 댈러스의 위도와 경도
latitude = 32.7767
longitude = -96.7970

# NWS API 엔드포인트
points_url = f"https://api.weather.gov/points/{latitude},{longitude}"


try:
    # 위치 정보 가져오기
    response = requests.get(points_url)
    response.raise_for_status()
    data = response.json()
    
    # 위치 정보에서 예보 URL 추출
    forecast_url = data['properties']['forecast']
    
    # 예보 데이터 가져오기
    forecast_response = requests.get(forecast_url)
    forecast_response.raise_for_status()
    forecast_data = forecast_response.json()
    
    # 현재 날씨 정보 출력
    forecast_data_string = json.dumps(forecast_data, indent=4, sort_keys=True)
    print(forecast_data_string)

except requests.exceptions.RequestException as e:
    print(f"HTTP 요청 오류: {e}")
except KeyError as e:
    print(f"데이터 처리 오류: {e}")
except Exception as e:
    print(f"기타 오류: {e}")

결론

National Weather Service(NWS) 는 API를 사용하여 미국 내 다양한 날씨 데이터를 실시간으로 가져올 수 있으며, 더욱이 무료로 제공되고, API키를 따로 발급받지 않아도 되므로 사용이 간편하다.

또한 National Weather Service(NWS) API는 다양한 엔드포인트를 통해 위치 기반 날씨 정보, 경고 및 알림, 관측소 데이터 등을 제공하고, 간단한 HTTP 요청으로 데이터를 가져올 수 있으며, JSON 형식으로 응답을 반환되므로, 데이터 추가 가공도 손쉽게 처리할 수 있으니 잘 활용해 보자.

2 comments

  1. 예보데이터를 시간별로 받고싶은데 혹시 예시 코드 부탁드려도 되나요ㅜㅜ
    아니면 참고 문서 url이라도 부탁드려요..
    지금이 오후 1시라면
    앞으로 24시간 동안의 예보를 받는 코드를 짜서 1시간에 한번씩 돌려보려 하는데 방법 있을까요

Leave a Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다