개요
✅ 목표
YouTube Data API를 통해 등록한 구글 계정에서 구독한 YouTube Channel을 불러와, 해당 채널의 가장 최근 영상의 상세 정보를 불러온다.
✅ 준비 과정 및 개발 과정
- 사용할 구글 계정으로 YouTube에 접속하여 몇몇의 채널의 구독하기
- YouTube Data API 키 발급받기
- OAuth2 인증받기
: OpenSSL 인증서 발급 받아 flask 실행 서버를 https가 되게 함 - 코딩하기
- Redirect URL 등록 후 client_secret.json 프로젝트에 적용하기
- 서버 실행하기
✅ 코드 전문
https://github.com/HyeM207/YouTube-Data-API-Test
230331 YouTube API 연결
1. YouTube Data API 키 발급받기
GCP에서 project 등록 후 YouTube Data API 키 발급받기
2. OAuth 2.0 인증받기
YouTube Data API를 사용하기 위해서는 클라이언트쪽에서 OAuth2.0 인증을 받아야된다.
OAuth 2.0이란?
- OAuth는 주로 타서비스에서 제공하는 API를 이용하여 2차 개발물을 만들때, 해당 애플리케이션에 접근 권한을 부여할 수 있다.
- 주위에서 쉽게 볼 수 있는 사례는 naver와 google 계정 연동으로 서비스 로그인/회원가입 하는 사례이다.
- 표준 인증 프레임워크로, 쉽게 말해 인증을 위한 프로토콜이다. OAuth를 이용하면 id,pw등 사용자 정보 없이도 접근 권한을 가지고 인증을 할 수 있다
그 이유는?
- 인증 자격증명 가져오기 | YouTube Data API | Google Developers
- 여기 페이지를 보면 API를 사용하기 위해서는 2가지 유형의 자격 증명 중 적합한 것을 골라야되는데,
구현해야할 것이 “구글 계정의 YouTube에서 구독한 유튜버의 플레이리스트” 이므*, 비공개 사용자 데이터에 액세스하는 것과 같다. (내 계정은 비공개 사용자) - 즉, OAuth 2.0 토큰을 받아서YouTube Data API요청할 때 같이 전송해야된다.
그럼 OAuth 2.0 자격증명을 생성해보자.
3. 이해하기
나는 Flask를 통해 API를 사용할거라, flask 애플리케이션은 인증된 리디렉션 URI를 지정해야한다. (리디렉션 URI는 OAuth 2.0 서버가 응답을 전송할 수 있는 엔드포인트)
4. pip 설치하기
pip으로 google-auth 와 google-api-python-client 라이브러리 설치
pip install google-auth google-api-python-client google-auth-oauthlib
[ 설치 목록 ]
- google-auth
- google-auth-oauthlib
- google-api-python-client
5. OAuth2 credentials 만들기 및 json 파일 다운로드
(1) Google Cloud Console > project 선택 > 사용자 인증 정보 > ‘사용자 인증 정보 만들기’ 눌러서 oauth 2.0 만들기
글쓴이의 경우 firebase를 연동하며 자동으로 만들어진게 있었다. 확인해보니 웹 어플리케이션으로 되어 있어서 추가로 설정해줄 것도 없었다.
(2) GCP에서 oauth에 redirect url에 만든 url 등록하기
OAuth 2.0의 경우 https 환경에서 요청을 해야지만 가능하므로, flask 웹서버 실행시 http가 아닌 https 로 실행되도록 바꾸는 작업이 필요하다.
https로 실행되도록 하는 방법이 여러개 있는데 나는 openssl로 키를 발급 받아서 하는 방법을 선택하였다.
(flask에서 https 적용하는 방법은 아래 첨부해두었다.)
(3) client secret JSON 파일을 다운로드
위쪽 사진의 'OAuth 2.0 클라이언트 ID'에 있는 'Web Client'의 맨 오른쪽에 있는 다운로드 버튼 누르면 json 파일 다운로드 가능하다. 받은 client secret JSON 파일을 flask project 폴더에 넣어줌
(만약 리디렉션 URL을 변경했다면 client_secret.json 파일을 다시 다운로드하여 프로젝트에 적용해줘야 한다.)
6. 서버 실행하기
서버 실행 시 openssl로 발급받은 키를 이용하여 실행하기 위해 다음과 같이 명령어를 입력한다.
'flask run --cert=cert.pem --key=key.pem'
(1) 완성된 코드 (flask > app.py)
from OpenSSL import SSL
import flask
from flask import redirect, url_for, render_template
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from google.oauth2 import id_token
import google.auth.transport.requests
import google.oauth2.credentials
import google_auth_oauthlib.flow
from config import CLIENT_ID ,SECRET_KEY
CLIENT_SECRETS_FILE = "client_secret.json"
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'
app = flask.Flask(__name__)
app.secret_key = SECRET_KEY
@app.route('/')
def index():
# print("확인 : ", CLIENT_ID ,SECRET_KEY)
if 'credentials' not in flask.session:
return flask.redirect('authorize')
return flask.redirect(flask.url_for('subscriptions'))
@app.route('/authorize')
def authorize():
# print("[authorize] 시작")
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
prompt='consent',
include_granted_scopes='true')
flask.session['state'] = state
return flask.redirect(authorization_url)
@app.route('/oauth2callback')
def oauth2callback():
# print("[oauth2callback] 시작")
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
# 세션에 credentials 정보 저장
credentials = flow.credentials
flask.session['credentials'] = {
'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'scopes': credentials.scopes
}
return flask.redirect(flask.url_for('subscriptions'))
'''
내가 구독한 계정 리스트 반환 -> 각 채널별 최근 video_id 추출 -> video_id로 상세 영상 정보 획득
# 참고 : api docs : https://developers.google.com/youtube/v3/docs/subscriptions/list?hl=ko
'''
@app.route('/subscriptions')
def subscriptions():
# print("[subscriptions] 시작")
if 'credentials' not in flask.session:
return redirect(url_for('index'))
credentials = Credentials.from_authorized_user_info(flask.session['credentials'], SCOPES)
youtube = build(API_SERVICE_NAME, API_VERSION, credentials=credentials)
subscriptions = []
request = youtube.subscriptions().list(
part='snippet',
mine=True,
order='alphabetical',
maxResults=50
)
print("[subscriptions] session 확인 ", flask.session['credentials'])
while request:
response = request.execute()
for item in response['items']:
subscriptions.append(item['snippet']['title'])
# ====== 구독한 channel Id로 가장 최근 video id 가져오기 ======
channel_id = [item['snippet']['resourceId']['channelId'] ]
videos_response = youtube.search().list(
part='id',
channelId=','.join(channel_id),
type='video',
order='date',
maxResults=1
).execute()
# video 응답 중 가장 최근의 videoID 추출
video_id = videos_response['items'][0]['id']['videoId']
print("\n\n채널명 : ", item['snippet']['title'], " video_id : " , video_id)
# ====== Video ID로 music track 불러오기 ======
musics_response = youtube.videos().list(
part='contentDetails',
id=video_id
).execute()
# response에서 음악 트랙 추출
content_details = musics_response['items'][0]['contentDetails']
if 'music' in content_details:
music_tracks = content_details['music']['songs']
print("music_tracks : " , music_tracks)
request = youtube.subscriptions().list_next(request, response)
return render_template('subscriptions.html', subscriptions=subscriptions)
if __name__ == '__main__':
# 계속 실행하다보니 session 쌓일거 같아서 실행할때마다 삭제해줌
flask.session.clear()
# OAuth 2.0인증하려면 Https 접속 필수 => SSL 인증서 받아서 적용해줌
context = SSL.Context(SSL.PROTOCOL_TLS)
context.load_cert_chain('cert.pem', 'key.pem')
app.run('localhost', 5000, ssl_context=context, debug=True)
(2) 프로젝트 구조
cert.pem과 key.pem은 openssl 증명서와 같다.
client_secret.json은 YouTube API를 사용하기 위한 OAuth 2.0 인증 시크릿 키이다.
(3) 실행 화면
[ 웹페이지 ]
로그인할 계정 선택하는 페이지 이후엔,
subscriptions 페이지로 넘어와 구독자 정보를 띄워주고,
콘솔창에는 해당 채널들의 최근 영상들의 정보를 보여준다.
[ 콘솔창 출력 일부 ]
채널명 : essential; video_id : RvvIquQTyLg {'kind': 'youtube#videoListResponse', 'etag': '6Z9ebEzgBe8uorDKx1B3S-0RwBg', 'items': [{'kind': 'youtube#video', 'etag': 'jk8zVUmNxBVB4tHnsz9mSuSSPzg', 'id': 'RvvIquQTyLg', 'snippet': {'publishedAt': '2023-03-31T05:00:09Z', 'channelId': 'UCSGC87iX0QhnIfUOI_B_Rdg', 'title': '[Playlist] 여유롭게 햇살을 즐겨볼까? 🌷 | 산뜻한 봄에 듣기 좋은 BGM | spring pop songs 🌷', 'description': 'Playlist by 벅스PD (벅스 뮤직PD)\n\n벅스에서 플레이리스트 를 확인해보세요!\n:: http://bugs.kr/!yt57278\\\\n\\\\n*벅스PD님의 감성이 내 취향이라면!\n:: http://bugs.kr/!yt57278pd\\\\n\\\\nmusic to make your day.\n\n#봄 #오후 #일상 #여유로운 #음악 #플레이리스트 #팝송', 'thumbnails': {'default': {'url': 'https://i.ytimg.com/vi/RvvIquQTyLg/default.jpg', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i.ytimg.com/vi/RvvIquQTyLg/mqdefault.jpg', 'width': 320, 'height': 180}, 'high': {'url': 'https://i.ytimg.com/vi/RvvIquQTyLg/hqdefault.jpg', 'width': 480, 'height': 360}, 'standard': {'url': 'https://i.ytimg.com/vi/RvvIquQTyLg/sddefault.jpg', 'width': 640, 'height': 480}, 'maxres': {'url': 'https://i.ytimg.com/vi/RvvIquQTyLg/maxresdefault.jpg', 'width': 1280, 'height': 720}}, 'channelTitle': 'essential;', 'tags': ['essential', '에센셜', '플레이리스트', 'playlist', '노래', '팝송', '음악', '이센셜', '추천', '플리', '벅스', '벅스뮤직', '화창한 ', '봄', '화창', '따뜻', '오후', '일상', '카페', '따사로운', '햇살', '낮', '점심', '스프링', 'pop', 'music', '팝송 플레이리스트', '팝', '팝송 추천', 'pop song', 'pop song playlist', '에센셜 봄', '봄에 듣기 좋은 노래', '봄 팝송'], 'categoryId': '10', 'liveBroadcastContent': 'none', 'localized': {'title': '[Playlist] 여유롭게 햇살을 즐겨볼까? 🌷 | 산뜻한 봄에 듣기 좋은 BGM | spring pop songs 🌷', 'description': 'Playlist by 벅스PD (벅스 뮤 직PD)\n\n벅스에서 플레이리스트를 확인해보세요!\n:: http://bugs.kr/!yt57278\\\\n\\\\n*벅스PD님의 감성이 내 취향이라면!\n:: http://bugs.kr/!yt57278pd\\\\n\\\\nmusic to make your day.\n\n#봄 #오후 #일상 #여유로운 #음악 #플레이리스트 #팝송'}, 'defaultAudioLanguage': 'ko'}, 'contentDetails': {'duration': 'PT45M9S', 'dimension': '2d', 'definition': 'hd', 'caption': 'false', 'licensedContent': False, 'contentRating': {}, 'projection': 'rectangular'}}], 'pageInfo': {'totalResults': 1, 'resultsPerPage': 1}}
채널명 : H녀 video_id : m59YICP9OhY {'kind': 'youtube#videoListResponse', 'etag': 'QIb07B4UkKPlqqF0KBU-xx2dTsg', 'items': [{'kind': 'youtube#video', 'etag': '58Nfwk77YLY155K_ucJInqrckWw', 'id': 'm59YICP9OhY', 'snippet': {'publishedAt': '2023-03-26T00:00:30Z', 'channelId': 'UCiKEFY49JIvpOOdTHgsWQHw', 'title': '✨요즘 틱톡에서 핫한 인기 팝송 모두 해석해버리기 | PLAYLIST', 'description': '이 노래 모르는 사람 손 🖐\n\n#틱톡 #틱톡노래 #playlist \n\n[👍구독] [🔔 알림] [❤️좋아요] [ ↪공유] [💬댓글] 큰 힘이 됩니다\n\n틱톡 랜덤 플레이 음원, 틱톡 랜덤 플레이 음원 2023, 틱톡 노래, 틱톡, 틱톡 노래 모음, 틱톡 팝송 모음, 팝송, 팝송 플 레이리스트, 팝송모음, 팝송 인기차트, 팝송 플리, 팝송 가사 해석, 팝송 추천, 팝송 해석, 팝송 가사, 팝송노래모음, playlist, pop, pop song', 'thumbnails': {'default': {'url': 'https://i.ytimg.com/vi/m59YICP9OhY/default.jpg', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i.ytimg.com/vi/m59YICP9OhY/mqdefault.jpg', 'width': 320, 'height': 180}, 'high': {'url': 'https://i.ytimg.com/vi/m59YICP9OhY/hqdefault.jpg', 'width': 480, 'height': 360}, 'standard': {'url': 'https://i.ytimg.com/vi/m59YICP9OhY/sddefault.jpg', 'width': 640, 'height': 480}, 'maxres': {'url': 'https://i.ytimg.com/vi/m59YICP9OhY/maxresdefault.jpg', 'width': 1280, 'height': 720}}, 'channelTitle': 'H녀', 'tags': ['틱톡 노래', '틱톡', '틱톡 노래 모음', '틱톡 랜 덤 플레이 음원', '틱톡 랜덤 플레이 음원 2023', '틱톡 팝송 모음', 'playlist', '팝송', '팝송 플레이리스트', '팝송모음', '팝송 인기차트', '팝송 플리', '팝송 가사 해석', '팝송 추천'], 'categoryId': '10', 'liveBroadcastContent': 'none', 'localized': {'title': '✨요즘 틱톡에서 핫한 인기 팝송 모두 해 석해버리기 | PLAYLIST', 'description': '이 노래 모르는 사람 손 🖐\n\n#틱톡 #틱톡노래 #playlist \n\n[👍구독] [🔔알림] [❤️좋아요] [↪공유] [💬댓글] 큰 힘이 됩니다\n\n틱톡 랜덤 플레이 음원, 틱톡 랜덤 플레이 음원 2023, 틱톡 노래, 틱톡, 틱톡 노래 모음, 틱톡 팝송 모음, 팝송, 팝송 플레이리스트, 팝송모 음, 팝송 인기차트, 팝송 플리, 팝송 가사 해석, 팝송 추천, 팝송 해석, 팝송 가사, 팝송노래모음, playlist, pop, pop song'}}, 'contentDetails': {'duration': 'PT49M56S', 'dimension': '2d', 'definition': 'hd', 'caption': 'false', 'licensedContent': False, 'regionRestriction': {'blocked': ['BY', 'RU']}, 'contentRating': {}, 'projection': 'rectangular'}}], 'pageInfo': {'totalResults': 1, 'resultsPerPage': 1}}
개발 중 만난 오류와 해결과정
1. Internal Server Error
찾아보니 oauth 인증할때 https 로 해야되는데 http로 요청할때 발생하는 오류라고 한다.
아래처럼 리디렉션 URL을 다 https로 수정해준다.
그리고 flask 웹 서버도 https로 실행되도록 한다.
만약 리디렉션 URL을 https로 해주고 flask는 https로 실행되지 않도록 설정하면 다음과 같이 '액세스 차단됨' 오류를 볼 수 있다.
2. 액세스 차단됨: 이 앱의 요청이 잘못 되었습니다.
이는 Google Cloud 페이지에서 설정한 리디렉션 URL 항목에 내가 실행한 프로젝트에서 접속하려는 URL이 없을때 발생한다.
이때 페이지의 '오류 세부 정보'를 누르면 내가 요청한 URL를 확인할 수있는데 해당 URL을 리디렉션 URL 항목에 추가하면 된다.
글쓴이의 경우는 https로 리디렉션 URL를 설정해두고, flask를 실행할때는 http로 실행하여 URL이 맞지 않아 해당 오류를 만났다.
이는 Flask에 Https를 OpenSSL로 적용하여 해결하였다.
3. 위의 과정까지 했는데도 oauth2callback에서 오류뜬다면?
- redirect url 설정 페이지에서 url 잘못된거 다 바꿔줌(https로 설정된것들)
- 그리고 session 삭제해주고, 제일 중요한게 authorization_url할때 prompt 옵션 consent로 추가해줌
(이거 안하고 계속 새로고침하면서 실행하면 session 출력시 refresh_token이 None으로 뜬다. 그래서 이 옵션을 줘서 세션이 새로운 접속으로 계속 업데이트 되도록 해줌)
Flask에 https 적용하기 with OpenSSL
(1) SSL 증명서 생성
(mac은 터미널에서 바로 입력하면되는데, windows는 다른 방법 이용하기)
윈도우의 경우에는 OpenSSL 사이트에 접속하여 다운로드 후, 환경변수 설정을 해주어야지만 터미널에서 openssl 명령어를 입력하여 사용할 수 있다.
<윈도우에서 OpenSSL 설치하는 법>
- openSSL을 해당 사이트(https://slproweb.com/products/Win32OpenSSL.html)에서 다운로드 후 설치하기
- 환경변수 PATH에 OpenSSL에 추가하기
- 잘 설치 됐는지 확인해보기 (cmd) `openssl version`
아래 명령어를 치면 키를 받을 수 있는데, 아래와 같이 인적사항을 기입하는 항목이 뜬다.
적당히 작성해주면 키 발급이 가능하다.
(key.pem은 365일 동안 유효하고 자체 서명된 SSL 증명서이다.)
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
(2) pip install pyopenssl
(3) flask에서 https 사용하도록 코딩하기
from OpenSSL import SSL
context = SSL.Context(SSL.PROTOCOL_TLSv1_2)
context.load_cert_chain('cert.pem', 'key.pem')
app.run(ssl_context=context)
flask 실행할때 명령어도 이제 바뀜 ⇒ flask run --cert=cert.pem --key=key.pem
'#️⃣ Project 및 개발일지 > Mini Project' 카테고리의 다른 글
[Python]GameMacro_순발력테스트 매크로 (0) | 2022.02.06 |
---|---|
[Python]WebCrawler2_웹툰 이미지 크롤링 (0) | 2022.02.06 |
[Python]WebCrawler1_홈페이지URL출력 (0) | 2022.02.06 |
[Python]Up&Down 게임 (0) | 2022.02.06 |