[Python] 공공 open API 실습 프로젝트

이예빈·2021년 5월 22일
5

API

목록 보기
2/3

⛅ 공공 open API 활용 - 동네의 날씨 데이터

지역에 해당하는 좌표 값을 입력하면 해당 동네의 기온과 날씨를 알려주는 프로그램

Open API 를 실습해보는 작고 간단한 프로젝트를 진행해 보았다.


멋사 조 스터디 운영진 선생님이 원하는 주제를 정해서 API 실습을 해보는 것을 과제로 내주셨다.


공공 OpenAPI 날씨 데이터

실습을 위해 내가 사용한 공공 데이터는 '공공데이터 포털'에서 사용 가능한 "기상청 동네예보 조회 서비스 API" 이다. ( 기상청 동네예보 조회 서비스 API 데이터)

기상청의 국가기후데이터센터에서 제공하는 REST API 를 활용하였다.
오픈 API의 상세 정보는 해당 사이트에서 캡처해온 다음 사진과 같다.

기상청으로부터 데이터는 아래와 같이 매일 8번 업데이트 된다.
해당 Base_time에 데이터가 업데이트 되면, API가 제공되는 시간은 10분 이후부터이다.



참고 문서를 열면 다음과 같이 한국의 전체 행정구역코드에 따라 지역의 x,y 좌표값 정보가 엑셀 파일로 담겨져있다.

이 엑셀 파일에서 찾고자 하는 동네의 (x,y) 좌표값을 찾아 그 값을 활용하였다. (내가 사는 지역을 기준으로, 경기 용인시(62, 120) 의 좌표를 예시로 활용할 것이다.)

공공 데이터 포털 사이트에서
api를 활용할 수 있도록 미리 제시한 파이썬 샘플 코드는 아래와 같다.

from urllib import request, urlopen
from urllib import urlencode, quote_plus

url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService/getUltraSrtNcst'
queryParams = '?' + urlencode({ quote_plus('ServiceKey') : '데이터 활용 인증 key값 여기다 붙여넣기', quote_plus('pageNo') : '1', quote_plus('numOfRows') : '10', quote_plus('dataType') : 'XML', quote_plus('base_date') : '20151201', quote_plus('base_time') : '0600', quote_plus('nx') : '18', quote_plus('ny') : '1' })

request = request(url + queryParams)
request.get_method = lambda: 'GET'
response_body = urlopen(request).read()
print(response_body)


구현과 실행 결과

파이썬의 라이브러리들을 사용하고, open API로부터 제공되는 데이터들을 가져오는 등
프로그램 코드를 구현하는 부분에 있어서 도움을 많이 얻은 블로그는
티스토리의 ai-creator - 날씨 정보 사용 API 프로젝트 글 이다.

위 블로그 글에 나온 것을 처음에는 따라해 보았고,
일부 코드를 수정/변경하여 내가 구현한 것은 다음과 같다.

import requests # HTTP 요청을 보내는 모듈
import json # json 파일 파싱하여 데이터 읽는 모듈
import datetime # 날짜시간 모듈
from datetime import date, datetime, timedelta # 현재 날짜 외의 날짜 구하기 위한 모듈

# 기상청_동네 예보 조회 서비스 api 데이터 url 주소
vilage_weather_url = "http://apis.data.go.kr/1360000/VilageFcstInfoService/getVilageFcst?"
# 실황정보를 조회하기 위해 발표일자, 발표시각, 예보지점 X 좌표, 예보지점 Y 좌표의 조회 조건으로
# 자료구분코드, 실황값, 발표일자, 발표시각, 예보지점 X 좌표, 예보지점 Y 좌표의 정보를 조회하는 기능

# 발급 받은 인증키 (Encoding Key) - secret.json 파일에 저장한 key값 읽음
with open('secret.json') as json_file:
    json_data=json.load(json_file)
# print(json_data)
service_key = json_data

now = datetime.now()
print("지금은", now.year, "년", now.month, "월", now.day, "일", now.hour, "시", now.minute, "분", now.second, "초입니다.")


print("날씨를 구하고자 하는 지역의 위도, 경도 값을 입력하시오 - ex) 용인 기흥구 (62 120)")
print("(x y) : ", end="")
nx,ny=input().split()

# 지역의 날씨 데이터 이용 (동네 좌표 값: nx, ny)
# nx = "62" - 용인 기흥 위도 좌표
# ny = "120" - 용인 기흥 경도 좌표

# 오늘
today = datetime.today() # 현재 지역 날짜 반환
today_date = today.strftime("%Y%m%d") # 오늘의 날짜 (연도/월/일 반환)
print('오늘의 날짜는', today_date)

# 어제
yesterday = date.today() - timedelta(days=1)
yesterday_date=yesterday.strftime('%Y%m%d')
print('어제의 날짜는', yesterday_date)

# 1일 총 8번 데이터가 업데이트 된다.(0200, 0500, 0800, 1100, 1400, 1700, 2000, 2300)
# 현재 api를 가져오려는 시점의 이전 시각에 업데이트된 데이터를 base_time, base_date로 설정
if now.hour<2 or (now.hour==2 and now.minute<=10): # 0시~2시 10분 사이
    base_date=yesterday_date # 구하고자 하는 날짜가 어제의 날짜
    base_time="2300"
elif now.hour<5 or (now.hour==5 and now.minute<=10): # 2시 11분~5시 10분 사이
    base_date=today_date
    base_time="0200"
elif now.hour<8 or (now.hour==8 and now.minute<=10): # 5시 11분~8시 10분 사이
    base_date=today_date
    base_time="0500"
elif now.hour<=11 or now.minute<=10: # 8시 11분~11시 10분 사이
    base_date=today_date
    base_time="0800"
elif now.hour<14 or (now.hour==14 and now.minute<=10): # 11시 11분~14시 10분 사이
    base_date=today_date
    base_time="1100"
elif now.hour<17 or (now.hour==17 and now.minute<=10): # 14시 11분~17시 10분 사이
    base_date=today_date
    base_time="1400"
elif now.hour<20 or (now.hour==20 and now.minute<=10): # 17시 11분~20시 10분 사이
    base_date=today_date
    base_time="1700" 
elif now.hour<23 or (now.hour==23 and now.minute<=10): # 20시 11분~23시 10분 사이
    base_date=today_date
    base_time="2000"
else: # 23시 11분~23시 59분
    base_date=today_date
    base_time="2300"

payload = "serviceKey=" + service_key + "&" +\
    "dataType=json" + "&" +\
    "base_date=" + base_date + "&" +\
    "base_time=" + base_time + "&" +\
    "nx=" + nx + "&" +\
    "ny=" + ny

# 값 요청 (웹 브라우저 서버에서 요청 - url주소와 )
res = requests.get(vilage_weather_url + payload)

items = res.json().get('response').get('body').get('items')

data = dict()
data['date'] = base_date

weather_data = dict()
for item in items['item']:
    # 기온
    if item['category'] == 'T3H':
        weather_data['tmp'] = item['fcstValue']
    
    # 기상상태
    if item['category'] == 'PTY':
        
        weather_code = item['fcstValue']
        
        if weather_code == '1':
            weather_state = '비'
        elif weather_code == '2':
            weather_state = '비/눈'
        elif weather_code == '3':
            weather_state = '눈'
        elif weather_code == '4':
            weather_state = '소나기'
        else:
            weather_state = '없음'
        
        weather_data['code'] = weather_code
        weather_data['state'] = weather_state

data['weather'] = weather_data

print()
#for i in data:
#    print(data[i])
# ex) {'code': '0', 'state': '없음', 'tmp': '17'} # 17도 / 기상 이상 없음

state=data['weather']['state']

print(data['date'][0:4],'년', data['date'][4:6], '월', data['date'][6:8],'일', base_time, '시의 날씨 데이터입니다.')
print("기온은", data['weather']['tmp'], "도 입니다.")

if state=='비':
    print('비가 와요. 우산을 꼭 챙겨주세요!')
elif state=='비/눈':
    print('비 또는 눈이 와요. 쌀쌀하니 따뜻하게 입어요! 우산도 꼭 챙겨주세요!')
elif state=='눈':
    print('눈이 와요. 장갑을 꼭 챙기세요!')
elif state=='소나기':
    print('소나기가 와요. 비가 언제 올지 모르니, 우산을 꼭 챙겨주세요!')
else:
    print('날씨가 좋네요 :)')



처음에는 현재 시각에 따라 API에 데이터를 요청하기 위한 시간에 대한 매개 변수를 결정하기 위해 다음과 같은 코드를 사용하였다.

# 1일 총 8번 데이터가 업데이트 된다.(0200, 0500, 0800, 1100, 1400, 1700, 2000, 2300)
# 현재 api를 가져오려는 시점의 이전 시각에 업데이트된 데이터를 base_time, base_date로 설정
if now.hour<2 or now.hour>23: # 23시~ 2시 사이
    base_date=yesterday_date # 구하고자 하는 날짜가 어제의 날짜
    base_time="2300"
elif now.hour<5: # 2시~5시 사이
    base_date=today_date
    base_time="0200"
elif now.hour<8: # 5시~8시 사이
    base_date=today_date
    base_time="0500"
elif now.hour<11: # 8시~11시 사이
    base_date=today_date
    base_time="0800"
elif now.hour<14: # 11시~14시 사이
    base_date=today_date
    base_time="1100"
elif now.hour<17: # 14시~17시 사이
    base_date=today_date
    base_time="1400"
elif now.hour<20: # 17시~20시 사이
    base_date=today_date
    base_time="1700" 
elif now.hour<23: # 20시~23시 사이
    base_date=today_date
    base_time="2000"

위와 같은 코드를 이용하면 다음과 같은 오류가 발생하였다.

05월 23일 약 밤 11시 0분 그리고 시간이 조금 지나 11시 1분 즈음에 데이터를 요청하였을 때, 코드 아래부분에 나타나 있는 것처럼, 'base_date'가 정의되어 있지 않다는 오류가 뜬다.
API에서 정보를 제공하는 것이 11시 10분이기 때문에,
11시부터 11시 10분 사이에, 11시에 대한 데이터를 요청하면 올바르게 값을 받아오지 못하는 큰 오류가 발생한다.

따라서 각 시간대의 10분 에 API가 제공된다는 것을 인지하고,
now.minute을 이용하여 hour 정보 뿐 아니라 minute 정보에 대해서도 올바르게 코드를 구현해야 한다.




API 제공 시간대에 맞춰, 현재 시각에 대하여 minute 분 단위를 판별하기 위해 코드를 다음과 같이 올바르게 수정하였다.

# 1일 총 8번 데이터가 업데이트 된다.(0200, 0500, 0800, 1100, 1400, 1700, 2000, 2300)
# 현재 api를 가져오려는 시점의 이전 시각에 업데이트된 데이터를 base_time, base_date로 설정
if now.hour<2 or (now.hour==2 and now.minute<=10): # 0시~2시 10분 사이
    base_date=yesterday_date # 구하고자 하는 날짜가 어제의 날짜
    base_time="2300"
elif now.hour<5 or (now.hour==5 and now.minute<=10): # 2시 11분~5시 10분 사이
    base_date=today_date
    base_time="0200"
elif now.hour<8 or (now.hour==8 and now.minute<=10): # 5시 11분~8시 10분 사이
    base_date=today_date
    base_time="0500"
elif now.hour<=11 or now.minute<=10: # 8시 11분~11시 10분 사이
    base_date=today_date
    base_time="0800"
elif now.hour<14 or (now.hour==14 and now.minute<=10): # 11시 11분~14시 10분 사이
    base_date=today_date
    base_time="1100"
elif now.hour<17 or (now.hour==17 and now.minute<=10): # 14시 11분~17시 10분 사이
    base_date=today_date
    base_time="1400"
elif now.hour<20 or (now.hour==20 and now.minute<=10): # 17시 11분~20시 10분 사이
    base_date=today_date
    base_time="1700" 
elif now.hour<23 or (now.hour==23 and now.minute<=10): # 20시 11분~23시 10분 사이
    base_date=today_date
    base_time="2000"
else: # 23시 11분~23시 59분
    base_date=today_date
    base_time="2300"

수정을 하고, 11시 10분이 되기 전 그리고 10분이 넘은 후, 다시 프로그램을 실행하여
올바르게 API 제공 데이터를 가져오나 확인을 해 보았다.
분 단위에 대해서도 시간을 체크하도록 구현하였더니,
아래와 같이 데이터를 제대로 읽어 온 것을 확인할 수 있다.

밤 11시 10분이 지나기 전, 11시 6분에는 저녁 8시에 대한 데이터를 가져왔다.


아래는 다음 날인 05월 24일, 11시 10분을 넘긴 11시 26분 즈음에 밤 11시에 대한 데이터를 가져와 올바르게 프로그램이 실행된 결과창이다.


비오는 날에 실행해보았더니,
짜잔!

위와 같이 우산을 챙기라는 문구도 나온다.




(각 코드 블록의 세부사항은 추가할 예정.)


✋ 그러나 ... 🤔🤨

고정된 (x,y) 좌표값을 이용해 해당 동네에 대한 날씨 정보를 얻는 것은 사실상 사용자 입장에서 매우 불편한 일이다. 찾고자 하는 동네의 좌표를 외우고 다니거나 엑셀 파일을 일일히 열어 지역을 일일히 검색하는 사람은 없기 때문이다..

따라서 추후에 검색창을 간단히 만들어 사용자로부터 지역의 이름을 직접 입력 받고, 해당 동네의 좌표값을 파일로부터 자동적으로 찾고 그 동네의 날씨 정보를 추출하도록 하는 방법을 고려해보고 있다. (나중에 차근차근 배우면서 구현해 볼 예정이다!)

혹은 지역을 검색하여 해당 지역의 위/경도값을 불러와 날씨 데이터를 추출하는 API를 이용해보는 방법도 고민 중에 있다.

(프론트엔드/백엔드를 비롯해 앞으로 더 많이 공부해야 내가 구현하고 싶은 것들을 구현해낼 수 있겠다!)



☑️ 추가 예정 / 추가하고 싶은 아이디어들

내가 추가하고자 하는 부분을 아래에 적어보았다.
체크 박스를 만들었으니, 열심히 공부해서 하나씩 실행해 나갈 때마다 체크 박스를 채워나갈 예정이다.

  • gitignore -> secret.json 파일 감추기
  • 지역 검색해서 위도/경도 추출하는 API 이용 (kakao api 또는 googlemaps api의 geocoding 사용)
  • 지도 map 이용 (kakao map api)
  • 사용자 입력: 검색 bar
  • 출력: 귀여운 날씨 아이콘(그림) 출력
  • 미세먼지 데이터 api & 내일 일기예보 데이터
  • 카카오톡 연결 (kakao service api)

✅ 앞으로 공부해야 할 것들

실습한 것들에 대해 기본적인 지식이 매우 미흡하기 때문에 기초부터 차근차근 배워나가야 될 것 같다.
위의 추가 아이디어 구현을 위해, 그리고 프로젝트와 관련해서 더 자세히 배우고 싶은 것들을 아래에 나열하였다. 멋사 조스터디 선생님이 올려주신 도움이 될만한 참고 링크들도 추가했다.

json과 requests 모듈

API

날씨 관련 api 프로젝트 영상

0개의 댓글