Skip to content

Open In Colab

Python Foundations
X1: The Spotify API

Instructor: Wesley Beckner

Contact: wesleybeckner@gmail.com

Solved: Notebook



For this workbook we're going to use the spotipy library to access the Spotify Web API!



Install and import libraries

First we will need to install it:

!pip install spotipy
Collecting spotipy
  Downloading spotipy-2.19.0-py3-none-any.whl (27 kB)
Collecting requests>=2.25.0
  Downloading requests-2.27.1-py2.py3-none-any.whl (63 kB)
     |████████████████████████████████| 63 kB 734 kB/s 
[?25hRequirement already satisfied: six>=1.15.0 in /usr/local/lib/python3.7/dist-packages (from spotipy) (1.15.0)
Collecting urllib3>=1.26.0
  Downloading urllib3-1.26.8-py2.py3-none-any.whl (138 kB)
     |████████████████████████████████| 138 kB 22.2 MB/s 
[?25hRequirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.25.0->spotipy) (2.10)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.25.0->spotipy) (2021.10.8)
Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.7/dist-packages (from requests>=2.25.0->spotipy) (2.0.11)
Installing collected packages: urllib3, requests, spotipy
  Attempting uninstall: urllib3
    Found existing installation: urllib3 1.24.3
    Uninstalling urllib3-1.24.3:
      Successfully uninstalled urllib3-1.24.3
  Attempting uninstall: requests
    Found existing installation: requests 2.23.0
    Uninstalling requests-2.23.0:
      Successfully uninstalled requests-2.23.0
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
google-colab 1.0.0 requires requests~=2.23.0, but you have requests 2.27.1 which is incompatible.
datascience 0.10.6 requires folium==0.2.1, but you have folium 0.8.3 which is incompatible.
Successfully installed requests-2.27.1 spotipy-2.19.0 urllib3-1.26.8

And then import

from spotipy import client
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials, SpotifyOAuth
import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Setup developer account

You'll need to visit this link to setup a developer account, then fill in your authorization information below

SPOTIPY_CLIENT_ID = ""
SPOTIPY_CLIENT_SECRET = ""

Top 10 tracks of an artist

We can grab the first 10 tracks of Led Zepplin:

lz_uri = 'spotify:artist:36QJpDe2go2KgaRleHCDTp'


results = spotify.artist_top_tracks(lz_uri)
ids = []

for i, track in enumerate(results['tracks']):
    ids.append(track['id'])
    if i < 5:
      print('track    : ' + track['name'])
      print('audio    : ' + track['preview_url'])
      print('cover art: ' + track['album']['images'][0]['url'])
      print()
track    : Stairway to Heaven - Remaster
audio    : https://p.scdn.co/mp3-preview/8226164717312bc411f8635580562d67e191a754?cid=93cef3f9255042d7854a6014e0929504
cover art: https://i.scdn.co/image/ab67616d0000b273c8a11e48c91a982d086afc69

track    : Immigrant Song - Remaster
audio    : https://p.scdn.co/mp3-preview/8455599677a13017978dcd3f4b210937f0a16bcb?cid=93cef3f9255042d7854a6014e0929504
cover art: https://i.scdn.co/image/ab67616d0000b27390a50cfe99a4c19ff3cbfbdb

track    : Whole Lotta Love - 1990 Remaster
audio    : https://p.scdn.co/mp3-preview/ce11b19a4d2de9976d7626df0717d0073863909c?cid=93cef3f9255042d7854a6014e0929504
cover art: https://i.scdn.co/image/ab67616d0000b273fc4f17340773c6c3579fea0d

track    : Black Dog - Remaster
audio    : https://p.scdn.co/mp3-preview/9b76619fd9d563a48d38cc90ca00c3008327b52e?cid=93cef3f9255042d7854a6014e0929504
cover art: https://i.scdn.co/image/ab67616d0000b273c8a11e48c91a982d086afc69

track    : Kashmir - Remaster
audio    : https://p.scdn.co/mp3-preview/f3ca68c9ceaa3435d5bd55c0199ba0b09b916cce?cid=93cef3f9255042d7854a6014e0929504
cover art: https://i.scdn.co/image/ab67616d0000b273765b0617b572bdd1dbdc7d8e

the top tracks API only gives the top 10 tracks by an artist:

len(ids)
10

Spotify has an audio features API that can be used for ML or data visualization:

features = spotify.audio_features(ids)
features
[{'acousticness': 0.58,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/5CQ30WqJwcep0pYcV4AMNc',
  'danceability': 0.338,
  'duration_ms': 482830,
  'energy': 0.34,
  'id': '5CQ30WqJwcep0pYcV4AMNc',
  'instrumentalness': 0.0032,
  'key': 9,
  'liveness': 0.116,
  'loudness': -12.049,
  'mode': 0,
  'speechiness': 0.0339,
  'tempo': 82.433,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/5CQ30WqJwcep0pYcV4AMNc',
  'type': 'audio_features',
  'uri': 'spotify:track:5CQ30WqJwcep0pYcV4AMNc',
  'valence': 0.197},
 {'acousticness': 0.013,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/78lgmZwycJ3nzsdgmPPGNx',
  'danceability': 0.564,
  'duration_ms': 146250,
  'energy': 0.932,
  'id': '78lgmZwycJ3nzsdgmPPGNx',
  'instrumentalness': 0.169,
  'key': 11,
  'liveness': 0.349,
  'loudness': -10.068,
  'mode': 1,
  'speechiness': 0.0554,
  'tempo': 112.937,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/78lgmZwycJ3nzsdgmPPGNx',
  'type': 'audio_features',
  'uri': 'spotify:track:78lgmZwycJ3nzsdgmPPGNx',
  'valence': 0.619},
 {'acousticness': 0.0484,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/0hCB0YR03f6AmQaHbwWDe8',
  'danceability': 0.412,
  'duration_ms': 333893,
  'energy': 0.902,
  'id': '0hCB0YR03f6AmQaHbwWDe8',
  'instrumentalness': 0.131,
  'key': 9,
  'liveness': 0.405,
  'loudness': -11.6,
  'mode': 1,
  'speechiness': 0.405,
  'tempo': 89.74,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/0hCB0YR03f6AmQaHbwWDe8',
  'type': 'audio_features',
  'uri': 'spotify:track:0hCB0YR03f6AmQaHbwWDe8',
  'valence': 0.422},
 {'acousticness': 0.396,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/3qT4bUD1MaWpGrTwcvguhb',
  'danceability': 0.437,
  'duration_ms': 295387,
  'energy': 0.864,
  'id': '3qT4bUD1MaWpGrTwcvguhb',
  'instrumentalness': 0.0314,
  'key': 4,
  'liveness': 0.242,
  'loudness': -7.842,
  'mode': 0,
  'speechiness': 0.0904,
  'tempo': 81.394,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/3qT4bUD1MaWpGrTwcvguhb',
  'type': 'audio_features',
  'uri': 'spotify:track:3qT4bUD1MaWpGrTwcvguhb',
  'valence': 0.749},
 {'acousticness': 0.452,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/6Vjk8MNXpQpi0F4BefdTyq',
  'danceability': 0.483,
  'duration_ms': 517125,
  'energy': 0.615,
  'id': '6Vjk8MNXpQpi0F4BefdTyq',
  'instrumentalness': 0.000414,
  'key': 2,
  'liveness': 0.0512,
  'loudness': -8.538,
  'mode': 1,
  'speechiness': 0.0497,
  'tempo': 80.576,
  'time_signature': 3,
  'track_href': 'https://api.spotify.com/v1/tracks/6Vjk8MNXpQpi0F4BefdTyq',
  'type': 'audio_features',
  'uri': 'spotify:track:6Vjk8MNXpQpi0F4BefdTyq',
  'valence': 0.594},
 {'acousticness': 0.072,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/3MODES4TNtygekLl146Dxd',
  'danceability': 0.468,
  'duration_ms': 263333,
  'energy': 0.607,
  'id': '3MODES4TNtygekLl146Dxd',
  'instrumentalness': 0.000852,
  'key': 9,
  'liveness': 0.225,
  'loudness': -11.367,
  'mode': 1,
  'speechiness': 0.0336,
  'tempo': 98.429,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/3MODES4TNtygekLl146Dxd',
  'type': 'audio_features',
  'uri': 'spotify:track:3MODES4TNtygekLl146Dxd',
  'valence': 0.886},
 {'acousticness': 0.000582,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/4PRGxHpCpF2yoOHYKQIEwD',
  'danceability': 0.317,
  'duration_ms': 220561,
  'energy': 0.887,
  'id': '4PRGxHpCpF2yoOHYKQIEwD',
  'instrumentalness': 0.00258,
  'key': 9,
  'liveness': 0.0891,
  'loudness': -7.292,
  'mode': 1,
  'speechiness': 0.0375,
  'tempo': 169.613,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/4PRGxHpCpF2yoOHYKQIEwD',
  'type': 'audio_features',
  'uri': 'spotify:track:4PRGxHpCpF2yoOHYKQIEwD',
  'valence': 0.871},
 {'acousticness': 0.943,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/70gbuMqwNBE2Y5rkQJE9By',
  'danceability': 0.503,
  'duration_ms': 212161,
  'energy': 0.265,
  'id': '70gbuMqwNBE2Y5rkQJE9By',
  'instrumentalness': 0.045,
  'key': 2,
  'liveness': 0.0867,
  'loudness': -15.913,
  'mode': 1,
  'speechiness': 0.0333,
  'tempo': 78.044,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/70gbuMqwNBE2Y5rkQJE9By',
  'type': 'audio_features',
  'uri': 'spotify:track:70gbuMqwNBE2Y5rkQJE9By',
  'valence': 0.522},
 {'acousticness': 0.0382,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/0QwZfbw26QeUoIy82Z2jYp',
  'danceability': 0.476,
  'duration_ms': 166267,
  'energy': 0.717,
  'id': '0QwZfbw26QeUoIy82Z2jYp',
  'instrumentalness': 7.61e-05,
  'key': 9,
  'liveness': 0.0818,
  'loudness': -9.192,
  'mode': 1,
  'speechiness': 0.0949,
  'tempo': 93.584,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/0QwZfbw26QeUoIy82Z2jYp',
  'type': 'audio_features',
  'uri': 'spotify:track:0QwZfbw26QeUoIy82Z2jYp',
  'valence': 0.753},
 {'acousticness': 0.262,
  'analysis_url': 'https://api.spotify.com/v1/audio-analysis/4ItljeeAXtHsnsnnQojaO2',
  'danceability': 0.525,
  'duration_ms': 262748,
  'energy': 0.929,
  'id': '4ItljeeAXtHsnsnnQojaO2',
  'instrumentalness': 2.9e-05,
  'key': 9,
  'liveness': 0.0754,
  'loudness': -8.56,
  'mode': 0,
  'speechiness': 0.0784,
  'tempo': 163.503,
  'time_signature': 4,
  'track_href': 'https://api.spotify.com/v1/tracks/4ItljeeAXtHsnsnnQojaO2',
  'type': 'audio_features',
  'uri': 'spotify:track:4ItljeeAXtHsnsnnQojaO2',
  'valence': 0.556}]
pd.DataFrame(features)
danceability energy key loudness mode speechiness acousticness instrumentalness liveness valence tempo type id uri track_href analysis_url duration_ms time_signature
0 0.338 0.340 9 -12.049 0 0.0339 0.580000 0.003200 0.1160 0.197 82.433 audio_features 5CQ30WqJwcep0pYcV4AMNc spotify:track:5CQ30WqJwcep0pYcV4AMNc https://api.spotify.com/v1/tracks/5CQ30WqJwcep... https://api.spotify.com/v1/audio-analysis/5CQ3... 482830 4
1 0.564 0.932 11 -10.068 1 0.0554 0.013000 0.169000 0.3490 0.619 112.937 audio_features 78lgmZwycJ3nzsdgmPPGNx spotify:track:78lgmZwycJ3nzsdgmPPGNx https://api.spotify.com/v1/tracks/78lgmZwycJ3n... https://api.spotify.com/v1/audio-analysis/78lg... 146250 4
2 0.412 0.902 9 -11.600 1 0.4050 0.048400 0.131000 0.4050 0.422 89.740 audio_features 0hCB0YR03f6AmQaHbwWDe8 spotify:track:0hCB0YR03f6AmQaHbwWDe8 https://api.spotify.com/v1/tracks/0hCB0YR03f6A... https://api.spotify.com/v1/audio-analysis/0hCB... 333893 4
3 0.437 0.864 4 -7.842 0 0.0904 0.396000 0.031400 0.2420 0.749 81.394 audio_features 3qT4bUD1MaWpGrTwcvguhb spotify:track:3qT4bUD1MaWpGrTwcvguhb https://api.spotify.com/v1/tracks/3qT4bUD1MaWp... https://api.spotify.com/v1/audio-analysis/3qT4... 295387 4
4 0.483 0.615 2 -8.538 1 0.0497 0.452000 0.000414 0.0512 0.594 80.576 audio_features 6Vjk8MNXpQpi0F4BefdTyq spotify:track:6Vjk8MNXpQpi0F4BefdTyq https://api.spotify.com/v1/tracks/6Vjk8MNXpQpi... https://api.spotify.com/v1/audio-analysis/6Vjk... 517125 3
5 0.468 0.607 9 -11.367 1 0.0336 0.072000 0.000852 0.2250 0.886 98.429 audio_features 3MODES4TNtygekLl146Dxd spotify:track:3MODES4TNtygekLl146Dxd https://api.spotify.com/v1/tracks/3MODES4TNtyg... https://api.spotify.com/v1/audio-analysis/3MOD... 263333 4
6 0.317 0.887 9 -7.292 1 0.0375 0.000582 0.002580 0.0891 0.871 169.613 audio_features 4PRGxHpCpF2yoOHYKQIEwD spotify:track:4PRGxHpCpF2yoOHYKQIEwD https://api.spotify.com/v1/tracks/4PRGxHpCpF2y... https://api.spotify.com/v1/audio-analysis/4PRG... 220561 4
7 0.503 0.265 2 -15.913 1 0.0333 0.943000 0.045000 0.0867 0.522 78.044 audio_features 70gbuMqwNBE2Y5rkQJE9By spotify:track:70gbuMqwNBE2Y5rkQJE9By https://api.spotify.com/v1/tracks/70gbuMqwNBE2... https://api.spotify.com/v1/audio-analysis/70gb... 212161 4
8 0.476 0.717 9 -9.192 1 0.0949 0.038200 0.000076 0.0818 0.753 93.584 audio_features 0QwZfbw26QeUoIy82Z2jYp spotify:track:0QwZfbw26QeUoIy82Z2jYp https://api.spotify.com/v1/tracks/0QwZfbw26QeU... https://api.spotify.com/v1/audio-analysis/0QwZ... 166267 4
9 0.525 0.929 9 -8.560 0 0.0784 0.262000 0.000029 0.0754 0.556 163.503 audio_features 4ItljeeAXtHsnsnnQojaO2 spotify:track:4ItljeeAXtHsnsnnQojaO2 https://api.spotify.com/v1/tracks/4ItljeeAXtHs... https://api.spotify.com/v1/audio-analysis/4Itl... 262748 4

Add additional artists

Let's get some other artist data to compare with:

artist_dict = {'zep': '36QJpDe2go2KgaRleHCDTp',
               'tswift': '06HL4z0CvFAxyc27GXpf02',
               'debussy': '1Uff91EOsvd99rtAupatMP',
               'luttrell': '4EOyJnoiiOJ4vuNhSBArB2',
               'johnwill': '3dRfiJ2650SZu6GbydcHNb'}

color_dict = {'zep': 'tab:blue',
               'tswift': 'tab:green',
               'debussy': 'tab:orange',
               'luttrell': 'tab:red',
               'johnwill': 'tab:pink'}
ids = []
artists = []
colors = []
for artist, uri in artist_dict.items():
  results = spotify.artist_top_tracks('spotify:artist:' + uri)


  for i, track in enumerate(results['tracks']):
      ids.append(track['id'])
      artists.append(artist)
      colors.append(color_dict[artist])
      if i < 1:
        print('track    : ' + track['name'])
        print('cover art: ' + track['album']['images'][0]['url'])
        print()
track    : Stairway to Heaven - Remaster
cover art: https://i.scdn.co/image/ab67616d0000b273c8a11e48c91a982d086afc69

track    : All Too Well (10 Minute Version) (Taylor's Version) (From The Vault)
cover art: https://i.scdn.co/image/ab67616d0000b273318443aab3531a0558e79a4d

track    : Clair de Lune, L. 32
cover art: https://i.scdn.co/image/ab67616d0000b2736e7bb273ff9cb1de1e1d4d0a

track    : Twin Souls
cover art: https://i.scdn.co/image/ab67616d0000b2735dea3da9d2751a0fa7b23fd3

track    : Carol of the Bells
cover art: https://i.scdn.co/image/ab67616d0000b273a68c06155b7c3cf82b00cb96
features = spotify.audio_features(ids)
df = pd.DataFrame(features)
df['artist'] = artists
df['color'] = colors
feat_names = df.columns[:11]
print(feat_names)
Index(['danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness',
       'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo'],
      dtype='object')
df.head()
danceability energy key loudness mode speechiness acousticness instrumentalness liveness valence tempo type id uri track_href analysis_url duration_ms time_signature artist color
0 0.338 0.340 9 -12.049 0 0.0339 0.5800 0.003200 0.1160 0.197 82.433 audio_features 5CQ30WqJwcep0pYcV4AMNc spotify:track:5CQ30WqJwcep0pYcV4AMNc https://api.spotify.com/v1/tracks/5CQ30WqJwcep... https://api.spotify.com/v1/audio-analysis/5CQ3... 482830 4 zep tab:blue
1 0.564 0.932 11 -10.068 1 0.0554 0.0130 0.169000 0.3490 0.619 112.937 audio_features 78lgmZwycJ3nzsdgmPPGNx spotify:track:78lgmZwycJ3nzsdgmPPGNx https://api.spotify.com/v1/tracks/78lgmZwycJ3n... https://api.spotify.com/v1/audio-analysis/78lg... 146250 4 zep tab:blue
2 0.412 0.902 9 -11.600 1 0.4050 0.0484 0.131000 0.4050 0.422 89.740 audio_features 0hCB0YR03f6AmQaHbwWDe8 spotify:track:0hCB0YR03f6AmQaHbwWDe8 https://api.spotify.com/v1/tracks/0hCB0YR03f6A... https://api.spotify.com/v1/audio-analysis/0hCB... 333893 4 zep tab:blue
3 0.437 0.864 4 -7.842 0 0.0904 0.3960 0.031400 0.2420 0.749 81.394 audio_features 3qT4bUD1MaWpGrTwcvguhb spotify:track:3qT4bUD1MaWpGrTwcvguhb https://api.spotify.com/v1/tracks/3qT4bUD1MaWp... https://api.spotify.com/v1/audio-analysis/3qT4... 295387 4 zep tab:blue
4 0.483 0.615 2 -8.538 1 0.0497 0.4520 0.000414 0.0512 0.594 80.576 audio_features 6Vjk8MNXpQpi0F4BefdTyq spotify:track:6Vjk8MNXpQpi0F4BefdTyq https://api.spotify.com/v1/tracks/6Vjk8MNXpQpi... https://api.spotify.com/v1/audio-analysis/6Vjk... 517125 3 zep tab:blue

Visualize the audio features

df.groupby('artist')[feat_names].mean()
danceability energy key loudness mode speechiness acousticness instrumentalness liveness valence tempo
artist
debussy 0.2868 0.019716 3.6 -30.3510 0.8 0.04209 0.990800 0.927000 0.07817 0.07747 81.2996
johnwill 0.2783 0.151770 3.9 -20.4347 0.5 0.03766 0.903000 0.615920 0.18085 0.19182 87.2151
luttrell 0.5310 0.706000 4.5 -9.3468 0.7 0.03504 0.127682 0.669259 0.18714 0.16701 117.5059
tswift 0.5073 0.621700 4.2 -6.4014 0.9 0.05926 0.198158 0.000263 0.13591 0.40550 125.7815
zep 0.4523 0.705800 7.3 -10.2421 0.7 0.09121 0.280518 0.038355 0.17212 0.61690 105.0253
fig, ax = plt.subplots(figsize=(10,10))
df.groupby('artist')[feat_names].mean().plot(kind='barh', ax=ax)
<matplotlib.axes._subplots.AxesSubplot at 0x7f7e11316ad0>

png

we see that it is difficult to see all the features together at once. One tactic may be to normalize the features

scaled = df[feat_names].apply(lambda x: (x - x.mean()) / (x.std()))
scaled['artist'] = df['artist']
fig, ax = plt.subplots(figsize=(10,10))
scaled.groupby('artist')[feat_names].mean().plot(kind='barh', ax=ax)
<matplotlib.axes._subplots.AxesSubplot at 0x7f7e110ac550>

png

We can then investigate if these scaled and centered features separate out under a dimensionality reduction (a topic we explore in unsupervised learning):

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(scaled[feat_names])
PCA(n_components=2)
X_pca = pca.transform(df[feat_names])

plt.scatter(X_pca[:, 0], X_pca[:, 1], alpha=0.8, c=df['color'].values,
            edgecolor='grey')
plt.xlabel('First PC')
plt.ylabel('Second PC')
Text(0, 0.5, 'Second PC')

png

🏋️‍♀️ Exercises

🕵️‍♀️ Exercise 1: Find artist urls and build dataset

a. Navigate to the Spotify web application, pick 5 artists and update the the dictionary below

# Cell for 1.a

artist_dict = {'zep': '36QJpDe2go2KgaRleHCDTp',
               'tswift': '06HL4z0CvFAxyc27GXpf02',
               'debussy': '1Uff91EOsvd99rtAupatMP',
               'luttrell': '4EOyJnoiiOJ4vuNhSBArB2',
               'johnwill': '3dRfiJ2650SZu6GbydcHNb'}

color_dict = {'zep': 'tab:blue',
               'tswift': 'tab:green',
               'debussy': 'tab:orange',
               'luttrell': 'tab:red',
               'johnwill': 'tab:pink'}

b. build feature set using spotify.artist_top_tracks('spotify:artist:' + uri) and storing the resultant track information

# Cell for 1.b

💫 Exercise 2: Visualize Features

a. Create a boxplot of each feature, grouped by artist (similar to example above)

# Cell for 2.a

b. Normalize the features this time, then create the boxplot

# Cell for 2.b

⚖️ Exercise 3: Write a function that returns a similarity score between two artists

# Cell for 3