본문 바로가기
사이드 프로젝트/음악추천 챗봇 서비스

음악추천 챗봇2. 음악데이터 파악하기 | Spotify API 이해

by 카프리썬_ 2021. 5. 27.
728x90

서버리스 음악추천 챗봇 서비스를 구현하기 위해 음악데이터가 필요하다. 

데이터를 수집하기 위해 Spotify API를 사용하기로 했다. 

 

이번엔 Spotify API의 구조를 알아보고, 어떤 api를 써서 어떤 데이터를 수집할 수 있을지 알아본다. 

그리고 마지막에 효율적인 데이터파이프라인 구축을 위해 데이터가 새어나가는 오류를 막기 위한 처리를 수행하였다. 


API란? 

Application Programming Interface

즉 두개의 시스템에 서로 상호작용하기 위한 그러니까 데이터를 주고받기 위한 인터페이스다. 

흔히 웹사이트는 http 프로토콜을 사용하는 rest api를 기반으로 구축되었다고 볼 수 있다.

웹을 통해 외부 서비스로부터 정보를 가져와서 뿌려주는 방식이기 때문이다. 

 

어떻게 사용하는가? 

리소스는 api를 통해 리턴된 정보이며 하나의 리소스안에 여러개의 endpoint가 존재한다. 

  • endpoint : 리소스를 액세스하는 경로/ 방법
  • method : 리소스접근에 허용된 행위 (GET/POST/PUT/DELETE/HEAD)

  • 파라미터 : endpoint를 통해 request를 할때 전달하는 옵션들이다. 

웹API는 HTTP프로토콜 규약을 따르기 떄문에 특정페이지에 접속하거나 정보를 가져오기 위해서

API에서요구하는대로 파라미터를 입력해야한다. 

이렇게 endpoint, method,파라미터에 따라서 어떤 데이터를 가져오는지가 다르다 


Spotify API

국내 멜론,지니와 같은 음악서비스인데  국제서비스이다. 

제공되는 곡들은 음반사들이 라이선스하여 합법적으로 제공한 것이다.

하지만, 사용자가 한 달 9.99 유로의 서비스 사용료를 내지 않는다면, 곡과 곡 사이에 광고가 삽입된다.

나는 한번도 써보지않은 음악서비스이지만, 최근에 국내서비스를 시작했다고 들어서 한번 들어는 본 적 있다. 

 

따로 개발자 사이트가 있따 https://developer.spotify.com/

여기에서 스포티파이 API에 접근권한이 있는 ID와 PW를 발급받아야한다. 

 

Spotify 가입 > 로그인 > Dashboard 

 

Spotify App생성

 

Spotify API 사용하기

API를 사용하기 위한 인증을 위해 client ID와 secret KEY를 획득해야한다. 

이 방식은 OAuth2.0방식이 아니라 Client Credentials Flow 방식이다. 

스포티파이 공식문서에는 여러가지 인증방식의 사용법에 대해 자세히 나와있는데 그중에서 이부분만 참고했다.

 

만들어 놓은 application(dash board)을 통해서 client id와 client secret을 spotify에게 주게 되는데,

spotify는 다시 한번 Access Token을 반환해 주게된다.

이 Access Token을 통해 data를 가져 올 수 있게 되는 것이다. 

 

그러니까 아래의 FLOW로 이해해보면 어플리케이션에서

POST https://accounts.spotify.com/api/token , Client id, client secret -> Spotify 인증센터 로 request를 한다.

허용되는 인증이면 그러니까 대시보드를 통해 정상적으로 발급받은 key들이면 인증센터에서 허가를 하고 response한다.

이때 response에는 “Access token을 headers에 넣어 보내는데 이걸 api를 사용할때 마다 쓰면 된다!1

 

Spotify API in Python (Token)

코드상에서 실제로  구현하면 이렇다.  다행히도 공식문서에 잘 나와있어서 참고 했다.

import requests
import base64
import json

client_id = "발급받은 client id"
client_secret = "발급받은 secret key"
endpoint = "https://accounts.spotify.com/api/token"

encoded = base64.b64encode("{}:{}".format(client_id, client_secret).encode('utf-8')).decode('ascii')

headers = {"Authorization": "Basic {}".format(encoded)}
payload = {"grant_type": "client_credentials"}

response = requests.post(endpoint, data=payload, headers=headers)
access_token = json.loads(response.text)['access_token']

#참고
print(json.loads(response.text))
{'access_token': 'BQ------', 'token_type': 'Bearer', 'expires_in': 3600}

이제 이렇게 access_token을 얻게 되면 API를 사용할 준비가 된 것이다. 

 

Spotify API로 방탄소년단 정보 가져오기

가져올 수 있는 여러가지 정보에 따라 여러API들이 있다.  

공식적으로 API문서도 나와있다. https://developer.spotify.com/documentation/web-api/reference/#category-search

 

reqeust는 어떤 파라미터를 받는지,  어떤걸 의미하는지 

response는 어떤걸 뱉는지 그떄 그 JSON구조는 어떤건지 쉽게 알 수 있다. 

 

또 Console탭으로 가면 사용할 수 있는 api 인터페이스 볼 수 있다. 

그래서 코드를 짤떄 어떻게 하면 될지 

 

예를 들어 Search API를 사용해보자.

개요 : 앨범, 아티스트,플레이리스트,트랙 등 매칭되는 키워드를 가지고 Spotify 카탈로그를 GET하는 API이다.

request 

  • HEADER:  Authorization -> 앞에서 인증받은 access_token이 header에 들어간다 
  • QUERY PARAMETER 
    • q : 검색할 키워드
    • type : 앨범, 아티스트,플레이리스트,트랙 등 타입 등등 옵션 키워드도 나와있다. 

response

이렇게 Items list안에 여러개의 값들이 json형식으로 들어와 있따.

그리고 그 값들을 구분하는 파라미터가 id이다

/ json response

{
    "artists": {
        "href": "https://api.spotify.com/v1/search?query=tania+bowra&offset=0&limit=20&type=artist",
        "items": [
            {
                "external_urls": {
                    "spotify": "https://open.spotify.com/artist/08td7MxkoHQkXnWAYD8d6Q"
                },
                "genres": [],
                "href": "https://api.spotify.com/v1/artists/08td7MxkoHQkXnWAYD8d6Q",
                "id": "08td7MxkoHQkXnWAYD8d6Q",
                "images": [
                    {
                        "height": 640,
                        "url": "https://i.scdn.co/image/f2798ddab0c7b76dc2d270b65c4f67ddef7f6718",
                        "width": 640
                    },
                    {
                        "height": 300,
                        "url": "https://i.scdn.co/image/b414091165ea0f4172089c2fc67bb35aa37cfc55",
                        "width": 300
                    },
                    {
                        "height": 64,
                        "url": "https://i.scdn.co/image/8522fc78be4bf4e83fea8e67bb742e7d3dfe21b4",
                        "width": 64
                    }
                ],
                "name": "Tania Bowra",
                "popularity": 0,
                "type": "artist",
                "uri": "spotify:artist:08td7MxkoHQkXnWAYD8d6Q"
            }
        ],
        "limit": 20,
        "next": null,
        "offset": 0,
        "previous": null,
        "total": 1
    }
}

Spotify API in Python (Search API)

방금 access_token을 얻는 부분을 함수로 변경했다. search API를 사용하기 위해서 access_token이 필요하기 때문이다.

이부분에서 조금 헤맸다. 그냥 헤더에 토큰을 넣는다고 생각했는데 공식문서에 명령어 예시를 보면 조금 다르다. 

앞에 Authorization": "Bearer [access_token] 이런식으로 명령어를 붙이는거 보고 코드를 수정했다. 

 

스포티파이의 웹콘솔에서 아래와 같은 명령어도 미리 볼 수 있었다 :

https://developer.spotify.com/console/get-search-item/?q=tania+bowra&type=artist 

 

그 다음에 api의 3요소(?) 즉 API를 사용하기 위해 아래의 요소를 확인해보았다. 

  • endpoint : https://api.spotify.com/v1/search"
  • method : GET 
  • 파라미터 : {'q':'BTS','type':'album','limit':5}
  • 헤더 : {"Authorization": "Bearer {}".format(token)}

그래서 이것만 알고 있으면 해당 API를 호출해서 정보를 가져올 수 있다.

def search(token):
    endpoint = "https://api.spotify.com/v1/search"
    
    headers = {"Authorization": "Bearer  {}".format(token)}
    query_params = {'q':'BTS','type':'album','limit':5}

    r=requests.get(endpoint,params=query_params,headers=headers)
    print(json.loads(r.text))

 


효율적인 데이터파이프라인을 위해서..

이제 스포티파이api를 통해 음악정보를 수집할 수 있는 솔루션은 완료되었다.

'어떻게 데이터를 가져올 것인가?'에 대한 1차적인 해답은 된 것 같다.

이제 "어떻게"를 조금 더 깊게 고민해봐야한다. 

 

아래는 조금더 나은? 데이터파이프라인을 위해 고민해봐야할 개선해야할 사항이다. 

  • API호출에 제한이 걸리면, 어떻게 데이터를 가져올 것인가?'
  • 중간에 에러가 발생하면, 어떻데 데이터를 가져올것인가? 
  • API인증키가 만료가 되면, 어떻데 데이터를 가져올것인가? 

데이터파이프라인이 중간에 끊어지면 안되기 때문에 위에 것들을 고민해봐야한다고 생각했다. 

여러가지 가능성을 열어두고 발생할 수 있는 예외들에 대해서 잘 대처할 수 있어야 효율적인 파이프라인을 만들 수 있꼬

유지보수도 수월하게 할 수 있기 때문이다.

그리고 파이프라인 구축에 있어서 에러가 적게 발생한다는 것은 파이프라인의 신뢰도를 향상시키는 요인이다. 

 

그래서 위의 내용까지 고려하여 코드를 수정했다. 

staus code에 따라서 어떻게 처리해야할지 추가했다. 

 

200이 success code이기 떄문에 그렇지 않은 상황에 대해서 로그를 남기도록 예외를 처리했다.

   if search_r.status_code!=200:
        logging.error(json.loads(search_r.text))
        if search_r.status_code == 429: #too much request
            retry_afer = json.loads(search_r.headers)['retry-After']
            time.sleep(int(retry_afer))
            search_r=requests.get(endpoint,params=query_params,headers=headers)
        elif search_r.code==401:
            headers = get_token(client_id,client_secret)
            search_r=requests.get(endpoint,params=query_params,headers=headers)
        else:
            logging.error(json.loads(search_r.text))

429는 너무 많은 데이터를 요청 했을 때 받는 status code

받은 출력안에 제한시간(retry-After)이 같이 존재하므로 그 시간동안 python 동작으로 멈추도록 sleep을 걸어두었다.

401은 access token이 expired되었을떄 받는 status code로 다시 client secret을 받도록 처리했다. 

 

여기 status code와 api호출 제한사항 >> 

https://developer.spotify.com/documentation/web-api/

 

 

참고 https://snepbnt.tistory.com/111?category=784758 

 

반응형