본문 바로가기

Programming/Python

[Python] OpenWeatherMap의 API를 이용한 세계 특정 도시의 날씨 출력하기

(* OpenWeatherMap의 로고)


 OpenWeatherMap(이하 OWM)은 현재 날씨라던가 예상 날씨(예보), 그리고 이전의 날씨를 축적해놓은 과거의 데이터 등등 날씨 관련 자료를 제공하는 온라인 서비스다. OWM은 또한 웹 서비스나 모바일 어플리케이션 개발자들을 위해 API를 (조건부)무료로 제공하고 있다. 


 API(Application Programming Interface)는 '응용 프로그램 프로그래밍 인터페이스'를 뜻하는 말로, 응용 프로그램에서 사용할 수 있도록 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 일컫는데, OWM의 예를 들어 웹 사이트(의 서비스)나 모바일 어플리케이션에서 OWM의 날씨 데이터를 이용한 서비스를 제작할 수 있도록 다양한 기능을 제공하는 것이라 생각하면 쉽다.


 '날씨라면 기상청의 API를 사용하면 되지 않는가?'라는 궁금증이 은연중에 떠오를 수 있으나, 일단 기상청의 그것은 거의 국내에만 한정된 동네예보 수준이고, API로만 봐도 다양한 형태의 요구에 맞는 서비스를 제공하지 못한다. 반면 OWM의 API 서비스는 상당히 잘 구성되어있고, 공적인 기상관측소 뿐만이 아닌 전세계의 수많은 개인 기상관측소들과도 연결된 덕에 제공하는 데이터의 정확성도 높은 편이라 할 수 있겠다.


 이런 OWM에서 제공하는 우수한 API를 이용해 세계 세계 특정 도시의 현재 날씨와 온도를 출력하는 프로그램을 파이썬(Python)을 이용해 작성해보자. 본 글은 파이썬 개발 환경 중 Anaconda 3.6버전을 기준으로 한다. 


 (※ 글 읽기가 귀찮다거나 시간이 없다…같은 사정이 있면 굵은 글씨로 적혀있는 부분만 읽어도 무방하다.)




1. API Key 얻기

 ① OWM 홈페이지(https://openweathermap.org)에 접속한 후 우측 상단 메뉴 중 API를 누른다. 아니면 다음 링크를 통해 직접 접속도 가능하다: https://openweathermap.org/api



 ② API 페이지에 접속한 모습이다. 현재 날씨 데이터부터 5일/3시간 간격과 16일/하루 간격의 예보 데이터, 과거의 날씨 데이터, 자외선 수치 등 다양한 기능의 API를 제공하고 있음을 알 수 있다.


 현재 날씨와 온도를 출력하는 프로그램을 작성하고자 하기 때문에, 여기서 우리에게 필요한 데이터는 현재 날씨 데이터이므로 Current weather data 섹션에 있는 Subscribe를 클릭한다.




 ③ 뜬금없이 가격이 적힌 페이지가 등장하지만 걱정하지 말자, 어쨌든 OWM은 API를 무료로도 제공하고 있으니. 앞서 "조건부" 무료라고 적어놨던 이유가 여기에 있다. 무료로도 거의 대부분의 기능을 사용할 수 있지만, 월 40달러~2000달러까지 요금을 결제하고 구독(Subscribe)하는 유료 서비스에 비하면 성능 및 기능적인 면에서 제약이 있다. 사이트 내의 표를 통해 무료 및 유료 서비스의 단계 별 지원하는 기능의 차이를 한 눈에 볼 수 있다.


 그래도 우리가 작성하고자 하는 프로그램에는 무료 서비스의 API만으로도 충분하기 때문에, Free 아래에 있는 Get API key and Start를 클릭한다.




 ④ 역시 세상에 완벽한 공짜는 없는 법. API 키를 얻기 위해서는 가입을 해야한다. 다만 회원가입 자체는 무지무지 단순해서 15초 안에 끝낼 수 있을 정도이니 잠시만 시간을 내보자.




 ※ 그런데 가입 후에 다시 본 주소로 돌아오면(http://openweathermap.org/appid) 로그인이 풀려있고, Sign Up을 누르면 이미 로그인이 되어있다며 나의 계정 페이지로 돌아오는, 이 과정이 뫼비우스의 띠마냥 반복되는 촌극이 벌어진다. 사이트 상의 오류이거나 기기마다 다르겠지만, 그래도 API 키를 얻을 수 있는 방법이 있으니 걱정하지 말자.


 로그인 후 바로 뜨는 나의 계정 페이지("My Home")에서 API keys를 눌러보자. (로그인이 되어있다면 다음 링크를 통해 바로 접속해도 된다: https://home.openweathermap.org/api_keys) 그러면 사진과 같이 API 키가 생성된 모습을 볼 수 있을 것이다. 그 외에도 특정한 이름을 정하고 API 키를 새로 생성할 수도 있다. 무료 및 스타트업(Startup) 이용자의 API 키는 생성 후 사용 가능하도록 활성화되기까지 10분가량 걸린다고 알려주고 있다.

 





2. PyOWM 사용하기

 PyOWM(Python wrapper around the OpenWeatherMap API)은 파이썬에서 OWM의 API를 쉽고 빠르게 접근하고 사용할 수 있도록 도와주는 래퍼(wrapper) 라이브러리이다. 우리가 만들고자 하는 프로그램과 그야말로 찰떡궁합이라 할 수 있겠다. 물론 사용하기 위해서는 설치가 필요하다.


 ① 시작 메뉴 -> 모든 프로그램 -> 보조 프로그램 -> 명령 프롬프트(cmd.exe)를 실행한 후[각주:1], python -m pip install pyowm을 입력한다. 대략 1분 내외로 설치되며, 마지막에 Successfully installed pyowm-2.6.1이 뜬다면 성공적으로 설치가 완료된 것이다.




 ② 이것으로 PyOWM 라이브러리 설치가 완료되었으니, 이제 사용하는 방법만 알면 된다. PyOWM의 GitHub 프로젝트 페이지에서 pyowm -> docs -> usage-examples.md로 들어가면 매뉴얼을 볼 수 있다.


 …아니면 다음 링크를 통해 바로 접속이 가능하다: 

 https://github.com/csparpa/pyowm/blob/master/pyowm/docs/usage-examples.md


 수많은 명령어와 그에 따른 기능 설명이 적혀있으나, '세계 특정 도시의 현재 날씨 및 온도 출력하기'라는 프로그램에 필요한 명령어들은 다음과 같이 최소한으로 압축할 수 있겠다.


>>> from pyowm import OWM

>>> API_key = '(사이트에서 생성한 API 키)'

>>> owm = OWM(API_key)


PyOWM 라이브러리를 불러온 뒤, API 키를 받음으로써 활성화될 OWM API 전역 객체(owm)를 설정(초기화)해준다.


>>> obs = owm.weather_at_place('London') 

>>> obs = owm.weather_at_id(2643741)

>>> obs = owm.weather_at_coords(-0.107331, 51.503614)


입력된 이름 ID(번호) / 좌표(위도, 경도)에 맞는 도시를 조회하고, 날씨 정보를 받아와 관측(Observation) 객체 obs에 할당받는다. 위 코드의 경우 런던을 예시로 작성되어있다.


>>> w = obs.get_weather()


obs에 할당된 특정 도시의 다양한 날씨 데이터에 접근할 수 있도록 만들어주는 코드이다.


>>> w.get_temperature()

{'temp': 293.4, 'temp_kf': None, 'temp_max': 297.5, 'temp_min': 290.9}

>>> w.get_temperature(unit='celsius')

>>> w.get_temperature('fahrenheit')


특정 도시의 현재 기온(temp_max: 최고 기온 / temp_min: 최저 기온) 데이터를 받아온다. 주의할 점으로 기온 데이터는 API에 딕셔너리 자료형으로 들어있기 때문에 반드시 get_temperature['temp']와 같이 대괄호를 사용해주어야 한다. 


기온의 단위는 섭씨(unit='celsius') 혹은 화씨('fahrenheit')로 설정 가능하다. 예를 들어, 현재 기온을 섭씨 단위의 데이터로 받아오고 싶다면 w.get_temperature(unit='celsius')['temp'] 로 사용하면 되겠다.


(※ 기온 데이터 중 'temp_kf'는 내부 파라미터로 딱히 신경쓰거나 건드릴 필요는 없다.)


>>> w.get_status()


특정 도시의 맑음 / 구름 / 안개 등과 같은 현재 날씨 데이터를 받아온다.





3. 코딩하기

 ① 프로그램을 작성하는 데 필요한 필수 코드들도 알게 됐으니, 이제 이걸 레고 조각을 다루듯 하나둘씩 조립해주면 된다. 우선 서울의 현재 날씨와 기온을 출력하는 아주 기본적이고 간단한 프로그램을 작성해보았다:


from pyowm import OWM
API_key = '(자신의 API 키)'
owm = OWM(API_key)
obs = owm.weather_at_place('Seoul')
w = obs.get_weather()

print('Seoul :', w.get_status(), w.get_temperature(unit='celsius')['temp'])


 위에서 각 코드별로 설명한 대로, 1번 라인 "from pyowm import OWM"부터 5번 라인 "w = obs.get_weather()"까지는 pyowm 라이브러리를 불러와 API 키를 통해 API에 접근하고 날씨 데이터를 받아오는 과정을 수행하기 때문에, 단 한 줄도 빠져서는 안 되는 필수 코드이다. 


 마지막으로 print 출력 코드로 도시 이름현재 날씨 및 기온(섭씨 출력/현재 기온 설정) 코드를 조합해주면 완성.


 결과는 이렇게 나온다:



 서울의 현재 날씨(Haze; 안개)와 기온(섭씨 3.72도)이 출력됐음을 알 수 있다. 




  이제 이 기본 소스와 '레고 조각'들을 응용하기만 하면 된다. 예를 들어 도시 이름을 인자로 받는 함수를 만들고 obs = owm.weather_at_place()에 그 인자를 넣어주는 프로그램을 만들어보자. 기존 소스를 아주 조금만 변형시켜주면 된다.


from pyowm import OWM
API_key = '(자신의 API 키)'
owm = OWM(API_key)

def weather(city):
obs = owm.weather_at_place(city)
w = obs.get_weather()
l = obs.get_location()
print(l.get_name()+':', w.get_status()+',', w.get_temperature(unit='celsius')['temp'], '˚C')
weather('Seoul')
weather('Tokyo')
weather('London')
weather('New York')
weather('Paris')
weather('Sydney')
weather('Nairobi')


 문자열로 된 도시 이름(city)을 인자로 받는 weather 함수를 생성하고 obs = owm.weather_at_place()의 인자를 직접 적는 대신 city로 받게끔 만든 소스 코드이다. 결과는 다음과 같다:




 ③ 최종적으로 서울부터 나이로비까지를 1번부터 7번까지 정하고, 번호를 입력받아 현재 날씨 및 기온을 출력하는 프로그램을 만들어보자. 마찬가지로 기존 소스를 조금만 변형시켜주면 되며, 여기에 약간의 알고리즘 구상만 해주면 되겠다.


from pyowm import OWM
API_key = '(자신의 API 키)'
owm = OWM(API_key)
city = ['Seoul', 'Tokyo', 'London', 'New York', 'Paris', 'Sydney', 'Nairobi']
def weather(num):
obs = owm.weather_at_place(city[num-1])
w = obs.get_weather()
l = obs.get_location()
print(l.get_name()+':', w.get_status()+',', w.get_temperature(unit='celsius')['temp'], '˚C')
print("1: Seoul / 2: Tokyo / 3: London / 4: New York \n"+"5: Paris / 6: Sydney / 7: Nairobi / 0: Exit")
while 1:
inum = int(input("Choose city number: "))
if(inum == 0):
print("Exit Program.")
break
elif(inum < 0 or inum > 7):
print("Out of Range.")
continue
weather(inum)


  * city = ['Seoul', 'Tokyo', 'London', 'New York', 'Paris', 'Sydney', 'Nairobi'] 

 -> 7개의 도시를 city라는 리스트(List) 자료형으로 생성한다. 리스트의 인덱스(index)는 0부터 시작하므로 city[0]은 'Seoul', city[1]은 'Tokyo' … city[5]는 'Sydney', city[6]은 'Nairobi'가 된다. 콘솔창과 Variable explorer를 통해 한 눈에 볼 수 있다:




 * obs = owm.weather_at_place(city[num-1])

 -> 앞서 언급했듯이 리스트의 인덱스(index)는 0부터 시작하기 때문에, 입력받은 값을 -1하여 city 인자의 인덱스값으로 받아온다. 그러면 서울 날씨를 알고자 1을 입력했을 시 city[(1-1)], 즉 city[0]이 인자로 들어가게 된다.


 * while 1

 -> 프로그램의 상시 작동을 위해 while 문을 이용한 무한루프를 생성한다. while 뒤에 조건으로 1 혹은 True를 붙이면 간단하게 무한루프를 만들 수 있다.


 * inum = int(input("Choose city number: "))

 -> inum 변수에 int형 숫자를 입력받는다.


 * if(inum == 0):

        print("Exit Program.")

        break

     elif(inum < 0 or inum > 7):

         print("Out of Range.")

         continue


     weather(inum)

 -> 만약 숫자 0을 입력받았다면 프로그램을 종료하고, 0 이하 혹은 7을 초과한 숫자를 입력받았을 경우 범위에서 벗어났음을 알리고 다시 숫자를 입력받는 예외 처리를 해준다. 1-7 사이의 범위 안에서 정상적으로 번호가 입력됐을 경우 함수 weather은 inum 인자를 받고 실행된다.


 프로그램의 결과는 이렇다:








4. PyOWM 없이도 API 데이터를 파싱하는 게 가능할까?

 물론 가능하지. urllib json이라는 라이브러리를 사용하는 방법이 있다. PyOWM과 마찬가지로 파이썬 라이브러리를 거치는 방식이긴 하지만, 적어도 이건 Anaconda3를 이용하는 사람이라면 따로 설치가 필요하지 않은 내장 라이브러리라는 장점이 있다.


 ① 먼저 API를 불러오는 방법부터 알아보자. OWM의 API는 JSON과 XML 포맷을 지원하고 있는데, 우리는 파이썬에서 사용하기 쉬운 JSON 포맷을 이용할 것이다. PyOWM을 쓰지 않는 이 방법에서는 앞서 언급한대로 API의 url 주소를 읽어오기 위한 urllib 라이브러리와, JSON 포맷으로 생성된 API를 파싱하기 위한 json 라이브러리를 사용한다.


 먼저 두 라이브러리를 불러와보자:


import urllib
import json



 ② urllib을 이용해 읽어올 API의 url 주소가 필요하다. 주소는 다음과 같은 구조를 가진다:


http://api.openweathermap.org/data/2.5/weather?q=(도시 이름)&APPID=(자신의 API 키)


그러니까 예를 들어 도시는 서울로 설정, api 키는 1234 라면 url 주소는: 

http://api.openweathermap.org/data/2.5/weather?q=Seoul&APPID=1234 가 된다.


  앞서 우리가 만들어보았던, 번호 선택에 따라 도시의 현재 날씨, 기온을 출력하는 프로그램을 편하게 짜고자 한다면, 도시 이름이 들어갈 부분을 따로 떼면 좋을 것이다. 그러면 url의 앞 부분과 도시 이름이 들어갈 부분, API 키가 들어갈 부분 - 이렇게 3등분을 해보자.


apiurl = 'http://api.openweathermap.org/data/2.5/weather?q='
city = ['Seoul', 'Tokyo', 'London', 'New York', 'Paris', 'Sydney', 'Nairobi']
apikey = '&APPID=(자신의 API 키)'



 ③ url 주소도 준비완료했으니, 이제 API를 파싱할 차례이다.


url = urllib.request.urlopen(apiurl + city[num-1] + apikey)
apid = url.read()
data = json.loads(apid)


 url 주소는 앞서 3등분을 했기 때문에 +연산을 이용해 풀 url을 만들어준다. 1번 라인은 urllib을 이용해 url 주소로 HTTP Request를 보내는 코드이다. 2번 라인은 HTTP Response 패킷을 받았을 때, url에 접근해 링크에 담긴 데이터를 따로 apid 변수에 저장한다. 3번 라인은 apid에 저장된 데이터를 json 라이브러리를 통해 파싱하도록 한다. 셋 중에서 단 한 줄이라도 빠지면 코드는 동작하지 않는다.


 즉, apid 변수에는 API의 raw한 데이터가 저장되고, data 변수에는 파싱된 데이터가 저장된다는 것. 이는 콘솔창과 Variable explorer를 통해 직접 확인해볼 수 있다:







 ④ API의 구조를 간략하게나마 뜯어보았기 때문에, 이제 data 딕셔너리로부터 프로그램에 사용할 도시 이름, 날씨, 기온 데이터를 참조하는 코드를 작성하면 된다.


cityname = data['name']
weather = data['weather'][0]['main']
temp = int(data['main']['temp'] - 273.15)


 딕셔너리에서 Key를 이용해 Value값을 얻고자 할 때 "딕셔너리 변수[Key]"를 사용한다. 예를 들어 homework = {'eng': 100, 'ko': 95}라는 딕셔너리 자료형이 있다고 하면, print(homework[eng])은 100을 출력하게 된다.


 그러므로 도시의 이름은 data 딕셔너리의 'name' Key 인덱스를 참조하면 되고, 날씨 데이터는 data 딕셔너리 -> weather 리스트의 -> 0번 딕셔너리(디폴트인 0번 사용) -> main 인덱스를 참조하면 되고, 기온 데이터는 data 딕셔너리 -> main 딕녀서리 -> temp 인덱스를 참조하면 된다. 


 temp 변수에 -273.15를 하는 이유는 (위의 ③-심화 파트에서 언급했다시피) 켈빈 단위로 저장된 기온 값을 섭씨로 변환하기 위해서이다. (섭씨 0도 = 273.15 K 이므로.)


 그렇다면 출력 코드는 다음과 같이 정리할 수 있겠다: (temp 변수에 문자열 변환(str)을 한 이유는 깔끔하게 섭씨 단위와 +연산을 해주기 위해서이다)


print(cityname+':', weather+',', str(temp)+'˚C')



 ⑤ 지금까지 적어온 코드 파편들과 PyOWM에 사용했던 소스 코드를 적절히 조합시켜 urllib/json 라이브러리를 이용한 최종 소스 코드를 작성해보자!


import urllib
import json
apiurl = 'http://api.openweathermap.org/data/2.5/weather?q='
city = ['Seoul', 'Tokyo', 'London', 'New York', 'Paris', 'Sydney', 'Nairobi']
apikey = '&APPID=(자신의 API 키)'
def weather(num):
url = urllib.request.urlopen(apiurl + city[num-1] + apikey)
apid = url.read()
data = json.loads(apid)
cityname = data['name']
weather = data['weather'][0]['main']
temp = int(data['main']['temp'] - 273.15)
print(cityname+':', weather+',', str(temp)+'˚C')
print("1: Seoul / 2: Tokyo / 3: London / 4: New York \n"+"5: Paris / 6: Sydney / 7: Nairobi / 0: Exit")
while 1:
try:
inum = int(input("Choose city number: "))
except ValueError:
print("Please enter a number only.")
continue
if(inum == 0):
print("Exit Program.")
break
elif(inum < 0 or inum > 7):
print("Out of Range.")
continue
weather(inum)


 프로그램을 실행한 결과는 다음과 같다:



 



5. 마무리

 OpenWeatherMap에서 제공하는 날씨 API를 이용해 세계 특정 도시의 날씨 정보를 출력하는 프로그램을 작성하는 법을 알아보았다. PyOWM을 이용한 쉬운 파싱과 내장 라이브러리 urllib/json을 이용한 '직접적'인 파싱 - 크게 두 가지 방법으로 진행해보았는데, 전자는 소스를 쉽고 비교적 눈에 잘 들어오도록 짤 수 있다는 장점이 있고, 후자는 약간 번거롭긴 하지만 API의 구조를 직접 들여다보고 탐구하도록 만드는, 씹고 뜯고 맛보고 즐기는 재미를 준다는 점에서 각각의 장단점이 있다고 생각하며, 자신에게 가장 알맞은 방법을 선택해 사용하면 될 것 같다.


 본 글은 기본적으로 현재 날씨와 온도를 출력하는 집중한 느낌이긴 하지만, API나 라이브러리의 매뉴얼를 참고하면서 해당 프로그램을 여러번 짜보고 분석하다보면 기능을 추가하거나 응용이 가능할 것이다. 처음이 어렵다는 말이 있듯이, 이번에 API의 사용법을 익히고 분석해봄으로써 국내외 수많은 종류의 API를 도전해볼 수도 있을 것이다. 많이 부족하지만, 이 글을 읽는 분들에게 도움이 되었으면 하는 바람이다.



  1. 아니면 시작 메뉴의 프로그램 및 파일 검색(윈도우 10에서는 Windows 검색)에 cmd를 입력해주자. 바로 명령 프롬프트가 떠오를 것이다. [본문으로]
  2. 유닉스 타임값을 우리에게 익숙한 '년월일 시:분:초' 구조로 출력하기 위해서는 datetime 라이브러리를 불러오고(import datetime) 다음 코드를 사용하면 된다. 할당된 변수 이름('data['dt']')은 모두 본 글을 기준으로 한 것이다: datetime.datetime.fromtimestamp(int(data['dt'])).strftime('%Y-%m-%d %H:%M:%S') [본문으로]