일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 업무자동화
- 파이썬게임
- 오늘배워오늘쓰는
- 독학
- 카카오
- Ai
- 빅데이터
- Quickstart
- 구글
- 간단한파이썬게임
- 딥러닝
- 구글일정
- Python
- 인공지능
- 웹크롤링
- 자동화
- kakao
- 소스코드
- 머신러닝
- 파이썬
- OpenAPI
- 기본기
- 크롤링
- Selenium
- 구글캘린더
- 파이썬게임만들기
- 음성인식
- 파이썬독학
- 파이썬간단한게임
- STT
- Today
- Total
ai-creator
OpenAPI 활용 - 일정 도우미 프로젝트 본문
늘 약속 시간만 되면, 무엇을 먹을지 몰라 고민 되시나요? OpenAPI를 사용하면 여러분의 고민을 덜 수 있습니다.
<< 목표 >>
- 구글 캘린더에서 식사 일정을 조회해서 주변 맛집을 찾아주는 일정 도우미 프로그램 만들기
<< 사전 준비 >>
사용할 API : 캘린더(구글), 카카오톡(카카오), 검색-지역(네이버)
언어 & 환경(IDE) : Python3.6 & Jupter notebook
예제파일 :
<< 구현 순서 >>
Step 0 | 구글 캘린더에 일정 등록 |
Step 1 | OpenAPI 인증 (구글 + 네이버 + 카카오) |
Step 2 | 구글 캘린더 일정 가져오기 |
Step 3 | 일정 데이터 정제하기 |
Step 4 | 맛집 검색 |
Step 5 | '나에게 카카오톡' 보내기 |
Step 6 | 전체 코드 |
Step 0) 구글 캘린더에 일정 등록
일정을 등록할 때, 제목을 특정 형식으로 지정합니다. 약속된 형식에서 "카테고리" 와 "장소" 정보를 추출해서 검색을 하는 용도입니다.
[카테고리-장소] 일정 제목
- 카테고리 : "식사" 로 설정되어 있다면, 맛집 검색이 필요하다고 인식합니다.
- 장소 : 장소(ex."강남역", "국민대")를 키워드로 네이버 검색을 합니다.
Step 1) OpenAPI 인증
Step 1-1) 구글 인증
# 1. 구글 API 인증
from google_auth_oauthlib.flow import InstalledAppFlow
# 구글 OAuth 클라이언트 ID Json 파일
gcreds_filename = 'auth/gcredentials.json'
# 캘린더에서 사용할 권한
SCOPES = ['https://www.googleapis.com/auth/calendar']
# 새로운 창에서 로그인 하시면 인증 정보를 얻게 됩니다.
flow = InstalledAppFlow.from_client_secrets_file(gcreds_filename, SCOPES)
gcreds = flow.run_local_server(port=0)
Step 1-2) 카카오 인증
# 2. 카카오톡 인증
# https://developers.kakao.com/docs/restapi/tool
# 해당 사이트에서 로그인 후 'Access token'을 얻어오세요
kcreds = {
"access_token" : "<Access token>"
}
kheaders = {
"Authorization": "Bearer " + kcreds.get('access_token')
}
Step 1-3) 네이버 인증
# 3. 네이버 인증
# 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')
}
Step 2) 구글 캘린더 일정 가져오기
일정을 등록할 때, 제목을 특정 형식으로 지정합니다. 약속된 형식에서 "카테고리" 와 "장소" 정보를 추출해서 검색을 하는 용도입니다.
[카테고리-장소] 일정 제목
- 카테고리 : "식사" 로 설정되어 있다면, 맛집 검색이 필요하다고 인식합니다.
- 장소 : 장소(ex."강남역", "국민대")를 키워드로 네이버 검색을 합니다.
# 사용할 기본 라이브러리 import
import datetime
import requests
import urllib
import json
# 구글 캘린더 API 서비스 객체 생성
from googleapiclient.discovery import build
service = build('calendar', 'v3', credentials=gcreds)
# 조회에 사용될 요청 변수 지정
calendar_id = 'primary' # 사용할 캘린더 ID
today = datetime.date.today().isoformat()
time_min = today + 'T00:00:00+09:00' # 일정을 조회할 최소 날짜
time_max = today + 'T23:59:59+09:00' # 일정을 조회할 최대 날짜
max_results = 1 # 일정을 조회할 최대 개수
is_single_events = True # 반복 일정의 여부
orderby = 'startTime' # 일정 정렬
# 오늘 일정 가져오기
events_result = service.events().list(calendarId = calendar_id,
timeMin = time_min,
timeMax = time_max,
maxResults = max_results,
singleEvents = is_single_events,
orderBy = orderby).execute()
events_result.get('items', [])
# events_result.get('items', [])
# [{'kind': 'calendar#event',
# 'etag': '"3161909706058000"',
# 'id': '71r7508o4t98naqhb41323lhpq',
# 'status': 'confirmed',
# 'htmlLink': 'https://www.google.com/calendar/event?eid=NzFyNzUwOG80dDk4bmFxaGI4123xdWxocHEgaWFtZXhhbXBsZTJAbQ',
# 'created': '2020-02-05T09:38:09.000Z',
# 'updated': '2020-02-06T02:07:33.029Z',
# 'summary': '[식사-국민대] 친구 만나기',
# 'description': '국민대에서 친구와 식사하기',
# 'location': '국민대학교, 대한민국 서울특별시 성북구 정릉동 정릉로 77',
# 'creator': {'email': 'example@gmail.com', 'self': True},
# 'organizer': {'email': 'example@gmail.com', 'self': True},
# 'start': {'dateTime': '2020-02-06T19:00:00+09:00'},
# 'end': {'dateTime': '2020-02-06T20:00:00+09:00'},
# 'iCalUID': '71r7508o4t98naqhb47rqulhpq@google.com',
# 'sequence': 1,
# 'reminders': {'useDefault': True}}]
가져온 일정(events_result)에서 사용할 주요 정보는 다음과 같습니다.
- summary : 일정 제목
- description : 일정 상세 설명
- location : 일정 주소 (구글 주소)
- start : 일정 시작 날짜
- end : 일정 종료 날짜
- htmlLink : 해당 일정에 접속하는 링크
- reminders : 일정을 알려주는 설정 정보입니다. useDefault : True 인 것은 해당 캘린더 전체에 적용된 기본 알림으로 하겠다는 뜻입니다.
Step 3) 일정 데이터 정제하기
# 테스트를 위해 오늘 일정에서 한 개만 가져오겠습니다.
items = events_result.get('items')
item = items[0]
# 일정 제목
gsummary = item.get('summary')
# 일정 제목에서 [식사-국민대]에서 카테고리와 장소만 빼내옵니다.
gcategory, glocation = gsummary[gsummary.index('[')+1 : gsummary.index(']')].split('-')
# 해당 구글 캘린더 일정 연결 링크
gevent_url = item.get('htmlLink')
gaddress = item.get('location')
# 구글 주소를 정제합니다.
# ex) "국민대학교, 대한민국 서울특별시 성북구 정릉동 정릉로 77" => "대한민국 서울특별시 성북구 정릉동 정릉로 77"
if ',' in gaddress:
gaddress = gaddress.split(',')[1].strip()
Step 4) 일정 위치 주변 맛집 검색
# '식사' 카테고리만 처리하기 원한다면 if문 안으로 코드를 넣으세요.
# if '식사' in category:
# 인코딩된 맛집 검색어
enc_location = urllib.parse.quote(glocation + "맛집")
# 검색에 사용될 정보
# sort : "comment"을 넣으면 블로그 리뷰순으로 정렬
# query : 검색어
# display : 검색 결과 출력 건수 지정 (기본값 : 1, 최대 : 5)
nparams = "sort=comment&query=" + enc_location + "&display=" + '5'
# 네이버 지역 검색 주소 및 검색할 정보
naver_local_url = "https://openapi.naver.com/v1/search/local.json?" + nparams
맛집 검색을 위해 필요한 정보들을 입력합니다.
# 네이버에 일정 장소 주변 맛집을 검색합니다.
res = requests.get(naver_local_url, headers=nheaders)
# 검색 결과로 받아온 10개의 결과
places = res.json().get('items')
places[:2]
# places[:2]
[{'title': '바람난오리궁뎅이',
'link': '',
'category': '한식>오리요리',
'description': '',
'telephone': '02-918-5999',
'address': '서울특별시 성북구 정릉동 767-4',
'roadAddress': '서울특별시 성북구 보국문로29길 15',
'mapx': '312191',
'mapy': '557556'},
{'title': '기차순대국',
'link': '',
'category': '한식>순대,순댓국',
'description': '',
'telephone': '02-914-9316',
'address': '서울특별시 성북구 정릉동 398-9',
'roadAddress': '서울특별시 성북구 보국문로11길 18-6',
'mapx': '312705',
'mapy': '556768'}]
Step 5) '나에게 카카오톡' 보내기
Step 5-1) 리스트 템플릿 형식 맞추기
받아온 결과를 이제 카카오톡 리스트 템플릿의 형식에 맞춰줍니다.
# 카카오톡 리스트 템플릿은 최대 3개를 표현하기 때문에 3개만 가져오겠습니다.
contents = []
for place in places[:3]:
ntitle = place.get('title') # 장소 이름
ncategory = place.get('category') # 장소 카테고리
ntelephone = place.get('telephone') # 장소 전화번호
nlocation = place.get('address') # 장소 지번 주소
# 각 장소를 클릭할 때 네이버 검색으로 연결해주기 위해 작성된 코드
enc_location = urllib.parse.quote(nlocation + ' ' + ntitle)
query = "query=" + enc_location
# 장소 카테고리가 카페이면 카페 이미지
# 이외에는 음식 이미지
if '카페' in ncategory:
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 ntelephone:
ntitle = ntitle + "\ntel) " + ntelephone
# 카카오톡 리스트 템플릿 형식에 맞춰줍니다.
content = {
"title": "[" + ncategory + "] " + ntitle,
"description": ' '.join(nlocation.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)
contents[0]
# contents[0]
# {'title': '[한식>오리요리] 바람난오리궁뎅이\ntel) 02-918-5999',
# 'description': '성북구 정릉동 767-4',
# 'image_url': 'https://freesvg.org/img/bentolunch.png?w=150&h=150&fit=fill',
# 'image_width': 50,
# 'image_height': 50,
# 'link': {'web_url': 'https://search.naver.com/search.naver?query=%EC%84%9C%EC%9A%B8%ED%8A%B9%EB%B3%84%EC%8B%9C%20%EC%84%B1%EB%B6%81%EA%B5%AC%20%EC%A0%95%EB%A6%89%EB%8F%99%20767-4%20%EB%B0%94%EB%9E%8C%EB%82%9C%EC%98%A4%EB%A6%AC%EA%B6%81%EB%8E%85%EC%9D%B4',
# 'mobile_web_url': 'https://search.naver.com/search.naver?query=%EC%84%9C%EC%9A%B8%ED%8A%B9%EB%B3%84%EC%8B%9C%20%EC%84%B1%EB%B6%81%EA%B5%AC%20%EC%A0%95%EB%A6%89%EB%8F%99%20767-4%20%EB%B0%94%EB%9E%8C%EB%82%9C%EC%98%A4%EB%A6%AC%EA%B6%81%EB%8E%85%EC%9D%B4'}
# }
카카오톡 리스트 템플릿
# 일정 주소 네이버 연결 링크
enc_gaddress = urllib.parse.quote(gaddress)
query = "query=" + enc_gaddress
gaddr_url = "https://search.naver.com/search.naver?" + query
# 카카오톡 리스트 템플릿을 작성해봅니다.
ktemplate = {
"object_type" : "list",
"header_title" : "'%s' - 맛집 추천" % gsummary,
"header_link" : {
"web_url": gevent_url,
"mobile_web_url" : gevent_url
},
"contents" : contents,
"buttons" : [
{
"title" : "일정 자세히 보기",
"link" : {
"web_url": gevent_url,
"mobile_web_url" : gevent_url
}
},
{
"title" : "일정 장소 보기",
"link": {
"web_url": gaddr_url ,
"mobile_web_url": gaddr_url
}
}
],
}
# json 형식의 string을 보내야하기 때문에 json.dumps로 dict => string(json)으로 바꿔줍니다.
kpayload = {
"template_object" : json.dumps(ktemplate)
}
template에 사용된 데이터
- header_title : 리스트 템플릿의 제목 (gsummary : 구글 캘린더에서 가져온 일정 제목)
- header_link : 리스트 템플릿의 제목 링크 (gevent_url : 구글 캘린더에서 가져온 일정 제목)
- - web_url : pc버전 카카오톡에서 사용하는 웹 링크 URL.
- - mobile_web_url : 모바일 카카오톡에서 사용하는 웹 링크 URL.
- contents : 리스트 템플릿에 표출될 내용 하나하나. (위에서 만든 맛집 정보를 담은 리스트)
- buttons
- - link : 버튼을 누르면 연결되는 페이지. 도메인 부분은 개발자 사이트에 등록된 도메인과 일치해야 합니다. (gaddr_url : 네이버에서 검색된 장소 정보)
Step 5-2) 도메인 등록하기
위에서 버튼을 클릭하면 이동하는 링크의 도메인을 내 앱 페이지에 등록해주지 않으면, 보안의 이유로 해당 링크로 이동시켜주지 않습니다.
> 순서 : 카카오 개발자 사이트 > 내 애플리케이션 > 플랫폼 > 사이트 도메인 수정 버튼 클릭 > https://search.naver.com 와 https://www.google.com 추가
Step 5-3) 카카오톡 보내기
위에 리스트 템플릿 형식에 맞춰진 데이터(kpayload)와 카카오톡 인증(kheaders) 그리고 템플릿 주소로 요청을 보내어 카카오톡을 보내면 결과를 확인할 수 있습니다!
# 기본 템플릿(피드, 리스트, 위치, 커머스, 텍스트) : https://kapi.kakao.com/v2/api/talk/memo/default/send
# 스크랩 템플릿 : https://kapi.kakao.com/v2/api/talk/memo/scrap/send
# 커스텀 템플릿 : https://kapi.kakao.com/v2/api/talk/memo/send
kakaotalk_template_url = "https://kapi.kakao.com/v2/api/talk/memo/default/send"
res = requests.post(kakaotalk_template_url, data=kpayload, headers=kheaders)
res.json()
# {'result_code': 0}
Step 6) 전체 코드
##################################################
# 1. 인증
##################################################
# 1-1. 구글 API 인증
from google_auth_oauthlib.flow import InstalledAppFlow
# 구글 OAuth 클라이언트 ID Json 파일
gcreds_filename = 'auth/gcredentials.json'
# 캘린더에서 사용할 권한
SCOPES = ['https://www.googleapis.com/auth/calendar']
# 새로운 창에서 로그인 하시면 인증 정보를 얻게 됩니다.
flow = InstalledAppFlow.from_client_secrets_file(gcreds_filename, SCOPES)
gcreds = flow.run_local_server(port=0)
# 1-2. 카카오톡 인증
# https://developers.kakao.com/docs/restapi/tool
# 해당 사이트에서 로그인 후 'Access token'을 얻어오세요
kcreds = {
"access_token" : "<Access token>"
}
kheaders = {
"Authorization": "Bearer " + kcreds.get('access_token')
}
# 1-3. 네이버 인증
# 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')
}
##################################################
# 2. 구글 캘린더 일정 가져오기
##################################################
# 기본 라이브러리 import
import datetime
import requests
import urllib
import json
# 구글 캘린더 API 서비스 객체 생성
from googleapiclient.discovery import build
service = build('calendar', 'v3', credentials=gcreds)
# 조회에 사용될 요청 변수 지정
calendar_id = 'primary' # 사용할 캘린더 ID
today = datetime.date.today().isoformat()
time_min = today + 'T00:00:00+09:00' # 일정을 조회할 최소 날짜
time_max = today + 'T23:59:59+09:00' # 일정을 조회할 최대 날짜
max_results = 1 # 일정을 조회할 최대 개수
is_single_events = True # 반복 일정의 여부
orderby = 'startTime' # 일정 정렬
# 오늘 일정 가져오기
events_result = service.events().list(calendarId = calendar_id,
timeMin = time_min,
timeMax = time_max,
maxResults = max_results,
singleEvents = is_single_events,
orderBy = orderby).execute()
events_result.get('items', [])
##################################################
# 3. 일정 데이터 정제하기
##################################################
# 테스트를 위해 오늘 일정에서 한 개만 가져오겠습니다.
items = events_result.get('items')
item = items[0]
# 일정 제목
gsummary = item.get('summary')
# 일정 제목에서 [식사-국민대]에서 카테고리와 장소만 빼내옵니다.
gcategory, glocation = gsummary[gsummary.index('[')+1 : gsummary.index(']')].split('-')
# 해당 구글 캘린더 일정 연결 링크
gevent_url = item.get('htmlLink')
gaddress = item.get('location')
# 구글 주소를 정제합니다.
# ex) "국민대학교, 대한민국 서울특별시 성북구 정릉동 정릉로 77" => "대한민국 서울특별시 성북구 정릉동 정릉로 77"
if ',' in gaddress:
gaddress = gaddress.split(',')[1].strip()
##################################################
# 4. 일정 위치 주변 맛집 검색
##################################################
# '식사' 카테고리만 처리하기 원한다면 if문 안으로 코드를 넣으세요.
# if '식사' in category:
# 인코딩된 맛집 검색어
enc_location = urllib.parse.quote(glocation + "맛집")
# 검색에 사용될 정보
# sort : "comment"을 넣으면 블로그 리뷰순으로 정렬
# query : 검색어
# display : 검색 결과 출력 건수 지정 (기본값 : 1, 최대 : 5)
nparams = "sort=comment&query=" + enc_location + "&display=" + '5'
# 네이버 지역 검색 주소 및 검색할 정보
naver_local_url = "https://openapi.naver.com/v1/search/local.json?" + nparams
# 네이버에 일정 장소 주변 맛집을 검색합니다.
res = requests.get(naver_local_url, headers=nheaders)
# 검색 결과로 받아온 10개의 결과
places = res.json().get('items')
places[0]
##################################################
# 5. 맛집 정보 카카오톡으로 보내기
##################################################
# 5-1. 리스트 템플릿 형식 맞추기
# 카카오톡 리스트 템플릿은 최대 3개를 표현하기 때문에 3개만 가져오겠습니다.
contents = []
for place in places[:3]:
ntitle = place.get('title') # 장소 이름
ncategory = place.get('category') # 장소 카테고리
ntelephone = place.get('telephone') # 장소 전화번호
nlocation = place.get('address') # 장소 지번 주소
# 각 장소를 클릭할 때 네이버 검색으로 연결해주기 위해 작성된 코드
enc_location = urllib.parse.quote(nlocation + ' ' + ntitle)
query = "query=" + enc_location
# 장소 카테고리가 카페이면 카페 이미지
# 이외에는 음식 이미지
if '카페' in ncategory:
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 ntelephone:
ntitle = ntitle + "\ntel) " + ntelephone
# 카카오톡 리스트 템플릿 형식에 맞춰줍니다.
content = {
"title": "[" + ncategory + "] " + ntitle,
"description": ' '.join(nlocation.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)
# 일정 주소 네이버 연결 링크
enc_gaddress = urllib.parse.quote(gaddress)
query = "query=" + enc_gaddress
gaddr_url = "https://search.naver.com/search.naver?" + query
gaddr_url
# 카카오톡 리스트 템플릿을 작성해봅니다.
ktemplate = {
"object_type" : "list",
"header_title" : "'%s' - 맛집 추천" % gsummary,
"header_link" : {
"web_url": gevent_url,
"mobile_web_url" : gevent_url
},
"contents" : contents,
"buttons" : [
{
"title" : "일정 자세히 보기",
"link" : {
"web_url": gevent_url,
"mobile_web_url" : gevent_url
}
},
{
"title" : "일정 장소 보기",
"link": {
"web_url": gaddr_url ,
"mobile_web_url": gaddr_url
}
}
],
}
# json 형식의 string을 보내야하기 때문에 json.dumps로 dict => string(json)으로 바꿔줍니다.
kpayload = {
"template_object" : json.dumps(ktemplate)
}
kpayload
# 5-2. 카카오톡 보내기
# 기본 템플릿(피드, 리스트, 위치, 커머스, 텍스트) : https://kapi.kakao.com/v2/api/talk/memo/default/send
# 스크랩 템플릿 : https://kapi.kakao.com/v2/api/talk/memo/scrap/send
# 커스텀 템플릿 : https://kapi.kakao.com/v2/api/talk/memo/send
kakaotalk_template_url = "https://kapi.kakao.com/v2/api/talk/memo/default/send"
res = requests.post(kakaotalk_template_url, data=kpayload, headers=kheaders)
res.json()
# {'result_code': 0}
<< 참고 자료 >>
<< Trouble Shooting >>
# python.exe exception 발생시
(만약 아나콘다를 사용하고 있다면)
$ conda update --all
$ conda update python
$ conda update anaconda
# 아래 함수 수행시 에러 발생시
events_result = service.events().list(calendarId = calendar_id,
timeMin = time_min,
timeMax = time_max,
maxResults = max_results,
singleEvents = is_single_events,
orderBy = orderby).execute()
에러에 주어진 링크를 클릭하여, api 활성화를 시켜준다.
저작물의 저작권은 작성자에게 있습니다.
공유는 자유롭게 하시되 댓글 남겨주세요~
상업적 용도로는 무단 사용을 금지합니다.
끝까지 읽어주셔서 감사합니다^^
'오늘 배워 오늘 쓰는 OpenAPI > 프로젝트' 카테고리의 다른 글
OpenAPI 활용 - 8시 뉴스 요약 봇 프로젝트 (2) | 2020.05.04 |
---|---|
OpenAPI 활용 - 날씨 정보를 사용한 맛집 추천 프로젝트 (41) | 2020.02.22 |
구글 OpenAPI 활용 - 캘린더 (2. 개념 : 함수/데이터 편) (5) | 2020.02.22 |
구글 OpenAPI 활용 - 캘린더 (1. 개념 : 자원편) (1) | 2020.02.22 |
카카오 OpenAPI 활용 - 나에게 카톡 메시지 보내기 (46) | 2020.02.22 |