Spotify API

Part 3. API๋Š” ๋ฌด์—‡์ธ๊ฐ€

๋ณธ ํฌ์ŠคํŒ…์€ ํŒจ์ŠคํŠธ์บ ํผ์Šค(FastCampus)์˜ ๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด๋ง ์˜ฌ์ธ์› ํŒจํ‚ค์ง€ Online์„ ์ฐธ๊ณ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

1. Spotify App ์ƒ์„ฑ ๋ฐ ํ† ํฐ ๋ฐœ๊ธ‰

Client Credentials Flow

Client Credentials Flow

1
2
3
4
5
{
   "access_token": "NgCXRKc...MzYjw",
   "token_type": "bearer",
   "expires_in": 3600,
}
  • client id, client secret์„ ์ œ๊ณตํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” 3600์ดˆ, ์ฆ‰ 1์‹œ๊ฐ„๋™์•ˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

2. Python ๊ธฐ๋ณธ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import sys

def main():
    print('fastcampus')

#python์œผ๋กœ ์‹คํ–‰ํ–ˆ์„ ๋•Œ, ํ•ด๋‹น pyํŒŒ์ผ ์ด๋ฆ„์ด ์ „๋‹ฌ๋˜๋ฉด, main()์„ ์‹คํ–‰ํ•˜๋ผ
if __name__ == '__main__':
    main()
#์ง์ ‘ pyํŒŒ์ผ์ด ์‹คํ–‰ ์•ˆ๋˜๊ณ , import spotify_api์™€ ๊ฐ™์ด ๋ชจ๋“ˆ์ฒ˜๋Ÿผ import๋˜๋ฉด, ~~๋ฅผ printํ•˜๋ผ.
else:
    print('this script i being imported')
  • Windows๋Š” Windows Powershell์„ ํ†ตํ•ด์„œ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์œ„์ฒ˜๋Ÿผ ์ฝ”๋”ฉ์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.

3. Python Requests ํŒจํ‚ค์ง€

requests python library > Developer Interface ์ฐธ๊ณ ํ•˜๊ธฐ
powershell์—์„œ pip install requests ์‹คํ–‰ํ•˜๊ธฐ

4. API๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์š”์ฒญ

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import sys
import requests
import base64
import json
import logging

client_id = '' # client_id ์ž…๋ ฅ
client_secret = '' # client_secret ์ž…๋ ฅ

def main():
    headers = get_headers(client_id, client_secret)
    params = {
        'q': 'BTS',
        'type': 'artist',
        'limit': 5
    }

    r = requests.get('https://api.spotify.com/v1/search', params=params, headers=headers)
    # print(r.status_code) # 200์ด๋ฉด ์ด์ƒ ์—†๋Š” ๊ฒƒ
    # print(r.text)
    # sys.exit(0)

def get_headers(client_id, client_secret):
    # 1์‹œ๊ฐ„๋งŒ ์žˆ์œผ๋ฉด expire๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€๋กœ function ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค์–ด๋‘๋Š” ๊ฒƒ์ด๋‹ค.
    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'
    }

    r = requests.post(endpoint, data=payload, headers=headers)
    # ์ค‘๊ฐ„์— ์ž˜ ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋Š” ์ฝ”๋“œ
    # print(r.status_code)
    # print(r.text)
    # print(type(r.text)) #string์œผ๋กœ ์ถœ๋ ฅ๋˜๋ฏ€๋กœ ์•„๋ž˜์—์„œ json.loads๋ฅผ ํ†ตํ•ด dictionary๋กœ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•œ๋‹ค.
    # sys.exit(0)

    access_token = json.loads(r.text)['access_token']
    headers = {
        'Authorization': "Bearer {}".format(access_token)
    }

    return headers

if __name__ == '__main__':
    main()

5. Status Code

  • Status Code๋ฅผ ์•Œ์•„์•ผ ํ•˜๋Š” ์ด์œ : ๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด์˜ ์ž˜๋ชป์ด ์•„๋‹Œ, Spotify ์„œ๋ฒ„์˜ ์˜ค๋ฅ˜ ๋“ฑ์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ์ธ์ง€ ์ฒดํฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Spotify Web API ๊ธฐ์ค€์ด์ง€๋งŒ, RFC 2616์™€ RFC 6585์— ์˜ํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ ํ†ต์šฉ๋˜๋Š” ๊ธฐ์ค€์ด๋‹ค.
STATUS CODE DESCRIPTION
200 OK - The request has succeeded. The client can read the result of the request in the body and the headers of the response.
201 Created - The request has been fulfilled and resulted in a new resource being created.
202 Accepted - The request has been accepted for processing, but the processing has not been completed.
204 No Content - The request has succeeded but returns no message body.
304 Not Modified. See Conditional requests.
400 Bad Request - The request could not be understood by the server due to malformed syntax. The message body will contain more information; see Response Schema.
401 Unauthorized - The request requires user authentication or, if the request included authorization credentials, authorization has been refused for those credentials.
403 Forbidden - The server understood the request, but is refusing to fulfill it.
404 Not Found - The requested resource could not be found. This error can be due to a temporary or permanent condition.
429 Too Many Requests - Rate limiting has been applied.
500 Internal Server Error. You should never receive this error because our clever coders catch them all โ€ฆ but if you are unlucky enough to get one, please report it to us through a comment at the bottom of this page.
502 Bad Gateway - The server was acting as a gateway or proxy and received an invalid response from the upstream server.
503 Service Unavailable - The server is currently unable to handle the request due to a temporary condition which will be alleviated after some delay. You can choose to resend the request again.

6. ์—๋Ÿฌ ํ•ธ๋“ค๋ง

sys.exit(0)๊ณผ sys.exit(1) ์ฐจ์ด
1
2
3
4
# ํ”„๋กœ๊ทธ๋žจ์„ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ
sys.exit(0)
# ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐ•์ œ์ ์œผ๋กœ ์ข…๋ฃŒ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ
sys.exit(1)
Status Code 401, 409 ์—๋Ÿฌ ํ•ธ๋“ค๋ง
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
r = requests.get('https://api.spotify.com/v1/search', params=params, headers=headers)
if r.status_code != 200:
    logging.error(r.text)

    ## Too many requests
    if r.status_code == 429:
        retry_after = json.loads(r.headers)['Retry-After']
        time.sleep(int(retry_after))

        r = requests.get('https://api.spotify.com/v1/search', params=params, headers=headers)

## access_token expireed
elif r.status_code == 401:
    headers = get_headers(client_id, client_secret)
    r = requests.get('https://api.spotify.com/v1/search', params=params, headers=headers)

else:
    sys.exit(1) #๊ฐ•์ œ์ข…๋ฃŒ

7. ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ•ธ๋“ค๋ง

์ฐธ๊ณ ์‚ฌ์ดํŠธ

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import sys
import requests
import base64
import json
import logging

client_id = '' # client_id ์ž…๋ ฅ
client_secret = '' # client_secret ์ž…๋ ฅ

def main():
    headers = get_headers(client_id, client_secret)

    # Get BTS' Albums
    r = requests.get('https://api.spotify.com/v1/artists/3Nrfpe0tUJi4K4DXYWgMUX/albums', headers=headers)
    raw = json.loads(r.text)

    # total = raw['total'] # ์ด 104๊ฐœ๊ฐ€ ์žˆ์Œ์„ ํ™•์ธ
    # offset = raw['offset'] # ์‹œ์ž‘์€ 0
    # limit = raw['limit'] # 20๊ฐœ์”ฉ ๋ฝ‘๊ฒ ๋‹ค.
    next = raw['next']

    albums = []
    albums.extend(raw['items'])

    ## ๋‚œ 200๊ฐœ๋งŒ ๋ฝ‘์•„ ์˜ค๊ฒ ๋‹ค.
    count = 0
    while count < 200 and next: # while next: ๋ผ๊ณ ๋งŒ ํ•˜๋ฉด ๋๊นŒ์ง€ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.
        r = requests.get(raw['next'], headers=headers)
        raw = json.loads(r.text)
        next = raw['next']
        # print(next) # ๋งจ ๋งˆ์ง€๋ง‰์—๋Š” none์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ๋Š” Spotify ํŽ˜์ด์ง€๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.
        albums.extend(raw['items'])
        count = len(albums)
    print(len(albums))

def get_headers(client_id, client_secret):
    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'}

    r = requests.post(endpoint, data=payload, headers=headers)
    access_token = json.loads(r.text)['access_token']
    headers = {'Authorization': "Bearer {}".format(access_token)}

    return headers

if __name__ == '__main__':
    main()