일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 기본기
- 파이썬게임만들기
- 구글일정
- 음성인식
- 구글캘린더
- 파이썬
- STT
- 카카오
- Ai
- 파이썬독학
- kakao
- 딥러닝
- 크롤링
- OpenAPI
- 파이썬간단한게임
- 파이썬게임
- 인공지능
- 자동화
- Quickstart
- 독학
- Selenium
- 오늘배워오늘쓰는
- 업무자동화
- 소스코드
- 구글
- 빅데이터
- 웹크롤링
- 머신러닝
- Python
- 간단한파이썬게임
- Today
- Total
ai-creator
OpenAPI 활용 - 날씨 정보를 사용한 맛집 추천 프로젝트 본문
/* 21년 09월 30일 부터 "동네예보"가 "단기예보"로 변경됩니다. 변경버전으로 업데이트 하였습니다." */
<< 문제 정의 >>
"오늘은 뭘 먹어야 하지?"
> "비 오는 날엔 파전이지!"
오늘 무엇을 먹어야 할지 고민이 될 때, 달라지는 날씨 정보를 이용해 주변에 맛집을 추천해주는 프로그램이 있다면 얼마나 좋을까요?
OpenAPI를 사용해서 만들어보겠습니다!
<< 목표 >>
- 날씨 정보(기온, 기상, 미세먼지)를 파악하여 맛집 추천해주기
<< 사전 준비 >>
- 카카오앱 사용할 도메인 추가(https://search.naver.com)
사용할 API: 공공데이터 포털(단기예보, 대기오염), 네이버 검색(지역) API, 카카오톡(텍스트, 리스트 템플릿)
언어 & 환경(IDE) : Python3.6 & Jupter notebook
예제파일명 : food_recommender.ipynb
공유폴더 : https://drive.google.com/drive/folders/10KXyPrJ_KEesvAvjnMpx1DS_md7gaN6H?usp=sharing
<< 구현 순서 >>
Step 1 | 공공데이터포털 API 신청 및 인증키 얻기 |
Step 2 | 날씨 정보 가져오기 (공공데이터포털) |
Step 3 | 미세먼지 정보 가져오기 (공공데이터포털 - ) |
Step 4 | 날씨에 따른 음식 데이터 구하기 |
Step 5 | 음식점 검색하기 (네이버 검색 OpenAPI) |
ㅑStep 6 | 카카오톡 보내기 |
Step 1) 공공데이터포털 API 신청 및 인증키 얻기
Step 1-1) 날씨정보
국가 공공데이터 포털에 접속해서 [로그인] 혹은 [회원가입]을 합니다.
검색 창에 "단기예보"를 입력해줍니다.
검색 결과 [오픈 API] 탭에서 [기상청 날씨예보 정보]로 이동합니다.
[활용신청]을 눌러줍니다.
활용목적을 반드시 작성하시고, [단기예보조회]가 선택되어 있는지 확인하고, 라이선스를 확인하여 [동의합니다]를 체크하고 [신청] 버튼을 눌러줍니다.
그럼 신청한 서비스를 이용할 수 있는 인증키를 확인하실 수 있습니다. 이 인증키를 잘 보관해주세요!
Step 1-2) 미세먼지
이번에는 미세먼지 정보도 신청해보겠습니다.
검색 창에 "한국환경공단 대기오염"을 검색하면 첫 번째로 검색됩니다.
[활용신청]을 누르시고
[상세기능정보]에서 [시도별 실시간 평균정보 조회]를 선택하고 아래 라이선스 동의와 [신청] 클릭
인증키를 확인하실 수 있습니다.
이제 날씨(단기예보)와 미세먼지(대기오염)를 각각 어떻게 가져오는지 확인해보겠습니다.
Step 2) 날씨 정보 가져오기
날씨 정보는 기온과 기상 상태 정보를 가져오도록 하겠습니다.
참고) 최고 1일 간의 자료만 제공합니다. |
요청시 필요한 URL 정보와 파라메터는 다음과 같습니다.
여기서 base_time은 문서를 보면 1일 총 8번 다음과 같은 시간에 표출됩니다. 예제는 0800시로 하겠습니다.
지역 정보(X,Y)는 첨부된 엑셀 파일에서 검색할 수 있습니다. 예제는 국민대 주소로 X:60 Y:128로 입력하겠습니다.
import requests
import json
import datetime
vilage_weather_url = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?"
service_key = "YOUR SERVICE KEY"
today = datetime.datetime.today()
base_date = today.strftime("%Y%m%d") # "20200214" == 기준 날짜
base_time = "0800" # 날씨 값
nx = "60"
ny = "128"
payload = "serviceKey=" + service_key + "&" +\
"dataType=json" + "&" +\
"base_date=" + base_date + "&" +\
"base_time=" + base_time + "&" +\
"nx=" + nx + "&" +\
"ny=" + ny
# 값 요청
res = requests.get(vilage_weather_url + payload)
items = res.json().get('response').get('body').get('items')
#{'item': [{'baseDate': '20200214',
# 'baseTime': '0500',
# 'category': 'POP',
# 'fcstDate': '20200214',
# 'fcstTime': '0900',
# 'fcstValue': '0',
# 'nx': 60,
# 'ny': 128},
# {'baseDate': '20200214',
# 'baseTime': '0500',
# 'category': 'PTY',
# 'fcstDate': '20200214',
# 'fcstTime': '0900',
# 'fcstValue': '0',
# 'nx': 60,
# 'ny': 128},
# 'ny': 128},
# {'baseDate': '20200214'
요청을 완료하면 다음과 같은 값들이 반환됩니다.
이 값들 중에서 PTY(강수형태)와 TMP(1시간 기온) 정보를 통해 비가 오는지, 기온은 어떠한지 알 수 있습니다.
PTY : 0-없음 1-비 2-비/눈 3-눈 4-소나기
data = dict()
data['date'] = base_date
weather_data = dict()
for item in items['item']:
# 기온
if item['category'] == 'TMP':
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
data['weather']
# {'code': '0', 'state': '없음', 'tmp': '9'} # 9도 / 기상 이상 없음
Step 3) 미세먼지 정보 가져오기
미세먼지의 PM10과 PM2.5를 가져와보도록 하겠습니다.
dust_url = "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst?"
service_key = "YOUR SERVICE KEY"
item_code_pm10 = "PM10"
item_code_pm25 = "PM25"
data_gubun = "HOUR"
search_condition = "WEEK"
payload = "serviceKey=" + service_key + "&" +\
"dataType=json" + "&" +\
"dataGubun=" + data_gubun + "&" +\
"searchCondition=" + search_condition + "&" +\
"itemCode="
# pm10 pm2.5 수치 가져오기
pm10_res = requests.get(dust_url + payload + item_code_pm10)
pm25_res = requests.get(dust_url + payload + item_code_pm25)
미세먼지는 XML 형식으로 제공되기 때문에 날씨 데이터를 다룰 때와는 다른 방식으로 파싱해야 합니다.
# xml 파싱하기
import xml.etree.ElementTree as elemTree
pm10_tree = elemTree.fromstring(pm10_res.text)
pm25_tree = elemTree.fromstring(pm25_res.text)
dust_data = dict()
for tree in [pm10_tree, pm25_tree]:
item = tree.find("body").find("items").find("item")
code = item.findtext("itemCode")
value = int(item.findtext("seoul"))
dust_data[code] = {'value' : value}
# 결과 값
dust_data
# {'PM10': {'value': 94}, 'PM2.5': {'value': 71}}
가져온 미세먼지(PM10)와 초미세먼지(PM2.5)를 기준에 맞춰 상태 값을 추가해주겠습니다.
# PM10 미세먼지 30 80 150
pm10_value = dust_data.get('PM10').get('value')
if pm10_value <= 30:
pm10_state = "좋음"
elif pm10_value <= 80:
pm10_state = "보통"
elif pm10_value <= 150:
pm10_state = "나쁨"
else:
pm10_state = "매우나쁨"
pm25_value = dust_data.get('PM2.5').get('value')
# PM2.5 초미세먼지 15 35 75
if pm25_value <= 15:
pm25_state = "좋음"
elif pm25_value <= 35:
pm25_state = "보통"
elif pm25_value <= 75:
pm25_state = "나쁨"
else:
pm25_state = "매우나쁨"
# 미세먼지가 나쁜 상태인지(1)/아닌지(0)
if pm10_value > 80 or pm25_value > 75:
dust_code = "1"
else:
dust_code = "0"
dust_data.get('PM10')['state'] = pm10_state
dust_data.get('PM2.5')['state'] = pm25_state
dust_data['code'] = dust_code
data['dust'] = dust_data
data['dust']
#{
# 'PM10': {'value': 94, 'state': '나쁨'},
# 'PM2.5': {'value': 71, 'state': '나쁨'}
#}
# 날씨 정보
# data
{
'weather': {
'code': '0', 'state': '없음', 'tmp': '9'
},
'date': '20200214',
'dust': {
'PM10': {'value': 94, 'state': '나쁨'},
'PM2.5': {'value': 71, 'state': '나쁨'},
'code': '1'
}
}
Step 4) 날씨에 따른 음식 데이터 구하기
구글에 검색해서 비 오는 날, 미세먼지 많은 날 추천 음식들을 모아봤습니다.
(음식은 검색하고 개인적으로 취합한 것이기에 여러분에 맞춰서 변경하길 추천합니다!)
비 오는 날/눈 오는 날
부대찌개, 아구찜, 해물탕, 칼국수, 수제비, 짬뽕, 우동, 치킨, 맥주, 국밥, 김치부침개, 두부김치, 파전
미세먼지
콩나물국밥, 고등어, 굴, 쌀국수, 마라탕
rain_foods = "부대찌개,아구찜,해물탕,칼국수,수제비,짬뽕,우동,치킨,국밥,김치부침개,두부김치,파전".split(',')
pmhigh_foods = "콩나물국밥,고등어,굴,쌀국수,마라탕".split(',')
해당 날씨의 음식 리스트에서 랜덤으로 추천해주고 평소 날씨의 경우에는 단순히 맛집으로 검색하여 추천해주겠습니다.
Step 5) 음식점 검색하기 - 네이버 검색 API
인증
# 네이버 인증
# https://developers.naver.com/apps
# 해당 사이트에서 로그인 후 "Cliend ID"와 "Client Secret"을 얻어오세요
ncreds = {
"client_id": "<CLIENT_ID>",
"client_secret" : "<CLIENT_SECRET>"
}
nheaders = {
"X-Naver-Client-Id" : ncreds.get('client_id'),
"X-Naver-Client-Secret" : ncreds.get('client_secret')
}
날씨 조건 설정
기상 : 비/눈/소나기 여부 | 미세먼지 : 높음 이상 여부 | 추천 결과 |
O | O | 경우 1: 비 오는 날 음식 3개 |
O | X | |
X | O | 경우 2: 미세먼지 음식 3개 |
X | X | 경우 3: 리뷰 순 맛집 음식 3개 추천 |
# 경우 1 : 비/눈/소나기 => 비오는날 음식 3개 추천
# 경우 2 : 초/미세먼지 나쁨 이상 => 미세먼지에 좋은 음식 3개 추천
# 경우 3 : 정상 => 블로그 리뷰 순 맛집 추천
# weather_state
if data.get('weather').get('code') != '0':
weather_state = '1'
elif data.get('dust').get('code') == '1':
weather_state = '2'
else:
weather_state = '3'
음식 리스트 뽑기
import random
# random.sample(x, k=len(x)) 무작위로 리스트 섞기
foods_list = None
# 경우 1, 2, 3
if weather_state == '1':
foods_list = random.sample(rain_foods, k=len(rain_foods))
elif weather_state == '2':
foods_list = random.sample(pmhigh_foods, k=len(pmhigh_foods))
else:
foods_list = ['']
foods_list
# ['쌀국수', '굴', '콩나물국밥', '마라탕', '고등어']
네이버 맛집 검색하기
import urllib
# urllib.parse.quote(query) URL에서 검색어를 인코딩하기 위한 라이브러리
# 네이버 지역 검색 주소
naver_local_url = "https://openapi.naver.com/v1/search/local.json?"
# 검색에 사용될 파라미터
# 정렬 sort : 리뷰순(comment)
# 검색어 query : 인코딩된 문자열
params_format = "sort=comment&query="
# 위치는 사용자가 사용할 지역으로 변경가능
location = "국민대"
# 추천된 맛집을 담을 리스트
recommands = []
for food in foods_list:
# 검색어 지정
query = location + " " + food + " 맛집"
# 지역검색 요청 파라메터 설정
params = "sort=comment" \
+ "&query=" + query \
+ "&display=" + '5'
# 검색
# headers : 네이버 인증 정보
res = requests.get(naver_local_url + params, headers=nheaders)
# 맛집 검색 결과
result_list = res.json().get('items')
# 경우 3 처리
# 맛집 검색 결과에서 가장 상위 3개를 가져옴
if weather_state == '3':
for i in range(0,3):
recommands.append(result_list[i])
break
# 경우 1,2 처리
# 해당 음식 검색 결과에서 가장 상위를 가져옴
if result_list:
recommands.append(result_list[0])
# 3개를 찾았다면 검색 중단
if len(recommands) >= 3:
break
# recommands
[{'title': '사이공본가 정릉점',
'link': '',
'category': '음식점>베트남음식',
'description': '',
'telephone': '02-914-0420',
'address': '서울특별시 성북구 정릉동 396-41',
'roadAddress': '서울특별시 성북구 보국문로11길 23 1층',
'mapx': '312677',
'mapy': '556720'},
{'title': '전주<b>콩나물국밥</b> 본점',
'link': '',
'category': '한식>국밥',
'description': '',
'telephone': '02-941-4733',
'address': '서울특별시 성북구 정릉동 284-11',
'roadAddress': '서울특별시 성북구 보국문로 75 대영빌딩',
'mapx': '312675',
'mapy': '556993'},
{'title': '태극쿵푸<b>마라탕</b>',
'link': '',
'category': '중식>중식당',
'description': '',
'telephone': '',
'address': '서울특별시 성북구 정릉동 344-4',
'roadAddress': '서울특별시 성북구 솔샘로6길 53-1',
'mapx': '312616',
'mapy': '556799'}]
Step 6) 카카오톡 보내기
인증
# 카카오톡 인증
# https://developers.kakao.com/docs/restapi/tool
# 해당 사이트에서 로그인 후 'Access token'을 얻어오세요
kcreds = {
"access_token" : "<ACCESS TOKEN>"
}
kheaders = {
"Authorization": "Bearer " + kcreds.get('access_token')
}
날씨 정보 카카오톡 보내기 (텍스트 템플릿)
import json
# 카카오톡 URL 주소
kakaotalk_template_url = "https://kapi.kakao.com/v2/api/talk/memo/default/send"
# 날씨 상세 정보 URL
weather_url = "https://search.naver.com/search.naver?sm=top_hty&fbm=0&ie=utf8&query=%EB%82%A0%EC%94%A8"
# 날씨 정보 만들기
text = f"""\
#날씨 정보 ({data['date']})
기온 : {data['weather']['tmp']}
기우 : {data['weather']['state']}
미세먼지 : {data['dust']['PM10']['value']} {data['dust']['PM10']['state']}
초미세먼지 : {data['dust']['PM2.5']['value']} {data['dust']['PM2.5']['state']}
"""
# 텍스트 템플릿 형식 만들기
template = {
"object_type": "text",
"text": text,
"link": {
"web_url": weather_url,
"mobile_web_url": weather_url
},
"button_title": "날씨 상세보기"
}
# JSON 형식 -> 문자열 변환
payload = {
"template_object" : json.dumps(template)
}
# 카카오톡 보내기
res = requests.post(kakaotalk_template_url, data=payload, headers=kheaders)
if res.json().get('result_code') == 0:
print('메시지를 성공적으로 보냈습니다.')
else:
print('메시지를 성공적으로 보내지 못했습니다. 오류메시지 : ' + str(res.json()))
추천 맛집 카카오톡 보내기 (리스트 템플릿)
# 리스트 템플릿 형식 만들기
contents = []
template = {
"object_type" : "list",
"header_title" : "현재 날씨에 따른 음식 추천",
"header_link" : {
"web_url": weather_url,
"mobile_web_url" : weather_url
},
"contents" : contents,
"buttons" : [
{
"title" : "날씨 정보 상세보기",
"link" : {
"web_url": weather_url,
"mobile_web_url" : weather_url
}
}
],
}
# contents 만들기
for place in recommands:
title = place.get('title') # 장소 이름
# title : 태극쿵푸<b>마라탕</b>
# html 태그 제거
title = title.replace('<b>','').replace('</b>','')
category = place.get('category') # 장소 카테고리
telephone = place.get('telephone') # 장소 전화번호
address = place.get('address') # 장소 지번 주소
# 각 장소를 클릭할 때 네이버 검색으로 연결해주기 위해 작성된 코드
enc_address = urllib.parse.quote(address + ' ' + title)
query = "query=" + enc_address
# 장소 카테고리가 카페이면 카페 이미지
# 이외에는 음식 이미지
if '카페' in category:
image_url = "https://freesvg.org/img/pitr_Coffee_cup_icon.png"
else:
image_url = "https://freesvg.org/img/bentolunch.png?w=150&h=150&fit=fill"
# 전화번호가 있다면 제목과 함께 넣어줍니다.
if telephone:
title = title + "\ntel) " + telephone
# 카카오톡 리스트 템플릿 형식에 맞춰줍니다.
content = {
"title": "[" + category + "] " + title,
"description": ' '.join(address.split()[1:]),
"image_url": image_url,
"image_width": 50, "image_height": 50,
"link": {
"web_url": "https://search.naver.com/search.naver?" + query,
"mobile_web_url": "https://search.naver.com/search.naver?" + query
}
}
contents.append(content)
# JSON 형식 -> 문자열 변환
payload = {
"template_object" : json.dumps(template)
}
# 카카오톡 보내기
res = requests.post(kakaotalk_template_url, data=payload, headers=kheaders)
if res.json().get('result_code') == 0:
print('메시지를 성공적으로 보냈습니다.')
else:
print('메시지를 성공적으로 보내지 못했습니다. 오류메시지 : ' + str(res.json()))
참고
Python 신규 버전(3.6 이상) 부터는 Literal String Interpolation 이라는, 간단히 줄여서 f-string 이라고 불리는 새로운 기능을 제공해 준다.
text = f"""\
#날씨 정보 ({data['date']}) \
기온 : {data['weather']['tmp']} \
기우 : {data['weather']['state']} \
미세먼지 : {data['dust']['PM10']['value']} {data['dust']['PM10']['state']} \
초미세먼지 : {data['dust']['PM2.5']['value']} {data['dust']['PM2.5']['state']} \
"""
python 3.5 이하 버전을 사용한다면, 아래와 같이 작성하면 같은 의미가 된다.
text = "#날씨 정보 (%s) \n" %(data['date']) \
+ "기온 : %s \n" %(data['weather']['tmp']) \
+ "기우 : %s \n" %(data['weather']['state']) \
+ "미세먼지 : %d %s \n" %(data['dust']['PM10']['value'], data['dust']['PM10']['state']) \
+ "초미세먼지 : %d %s \n" %(data['dust']['PM2.5']['value'], data['dust']['PM2.5']['state'])
저작물의 저작권은 작성자에게 있습니다.
공유는 자유롭게 하시되 댓글 남겨주세요~
상업적 용도로는 무단 사용을 금지합니다.
끝까지 읽어주셔서 감사합니다^^
'오늘 배워 오늘 쓰는 OpenAPI > 프로젝트' 카테고리의 다른 글
웹크롤링 - 웹크롤링 이란? (1/3) (0) | 2020.05.09 |
---|---|
OpenAPI 활용 - 8시 뉴스 요약 봇 프로젝트 (2) | 2020.05.04 |
OpenAPI 활용 - 일정 도우미 프로젝트 (10) | 2020.02.22 |
구글 OpenAPI 활용 - 캘린더 (2. 개념 : 함수/데이터 편) (5) | 2020.02.22 |
구글 OpenAPI 활용 - 캘린더 (1. 개념 : 자원편) (1) | 2020.02.22 |