Automating My Now Page Part 1: Playing with REST APIs in Python
One of my favorite parts of the IndieWeb is the now page—you can find mine here. A now page is where someone can list what they’re focusing on right now versus a general description of them that you might see on an “About” page.
It’s easy to manually update my now page, and portions of it should be intentionally written. However, it makes sense to automatically update some parts, like the “What I’m listening to” section. My music players are connected to Last.fm, so everything I listen to is automatically logged into their database. I want to find a way to pull data from Last.fm and upload it to omg.lol (where I host my now page), that way I can have an always-up-to-date list of my top artists for the world to see.
Thankfully, this is a solved problem! Both Last.fm and omg.lol provide REST APIs, so all I have to do is write some code to glue them together. I only have a little programming experience, but I can Google my way through writing a Python script, so I decided to learn how to interact with REST APIs with Python.
I want to break this project into parts to work around my other commitments and document each separately as I learn how to build it. My goal for this post is to learn how to get data from a RESTful API, specifically the top five artists I’ve listened to in the past week from the Last.fm API. I’ll work through updating my now page via the omg.lol API and making the process run periodically so my page is continuously updated.
Interacting with RESTful APIs
RESTful APIs use standard web technologies, namely HTTP requests. From my research, the requests library is one of the most popular, if not the most popular, ways to make HTTP requests with Python. I won’t go into detail about how to install Python, and the requests
documentation describes how to install the library. Once everything is set up, all it takes to use requests
is to import
it:
import requests
The Last.fm API
Last.fm has a well documented API; all you need to start is an API key. Once I had my API key, I stored it and the API root URL in variables to access them in later code.
root_url = 'http://ws.audioscrobbler.com/2.0/'
api_key = '*******'
Last.fm provides the method user.getTopArtists
to get the top artists listened to by a user. The method has the following parameters:
user (Required) : The user name to fetch top artists for.
period (Optional) : overall | 7day | 1month | 3month | 6month | 12month - The time period over which to retrieve top artists for.
limit (Optional) : The number of results to fetch per page. Defaults to 50.
page (Optional) : The page number to fetch. Defaults to first page.
api_key (Required) : A Last.fm API key.
Along with the parameters above, I put the method, user.gettopartists
, and the response format, JSON, into a Python dictionary:
params = {
'method': 'user.gettopartists',
'user': 'jeffreyflorek',
'period': '7day',
'api_key': api_key,
'format': 'json',
}
Now that I have the parameters, I can make an HTTP request using the requests.get()
function.
response = requests.get(root_url, params=params)
Since these are computers, things will go wrong. I added some code to check if the request was successful and print an error message if something went wrong.
if response.status_code == 200:
data = response.json()
else:
print(f"Request failed with status code {response.status_code}")
print(f"Reason: {response.reason}")
The response is formatted as JSON, but calling response.json()
returns a Python dictionary that looks like this:
{
"topartists": {
"artist": [
{
"streamable": "0",
"image": [
{
"size": "small",
"#text": "https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png",
},
# ... truncated for brevity ...
{
"size": "mega",
"#text": "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png",
},
],
"mbid": "ed84e235-0dd9-41c8-b9be-1e5cfc1198c4",
"url": "https://www.last.fm/music/Remember+Sports",
"playcount": "26",
"@attr": {"rank": "1"},
"name": "Remember Sports",
},
# ... more data here ...
],
"@attr": {
"user": "jeffreyflorek",
"totalPages": "3",
"page": "1",
"perPage": "50",
"total": "144",
},
}
}
This is a ton of data; I only need part for my now page. To make a list of just the artist names—and links to their Last.fm page, because why not—I can iterate over the data and format it as a Markdown list:
for artist in data['topartists']['artist']:
print((f"- [{artist['name']}]({artist["url"]})"))
Last.fm also provides a user.getWeeklyArtistChart
method. I’m unsure what the difference is between this and user.getTopArtists
, so let’s check it out. Here are the parameters:
user (Required) : The last.fm username to fetch the charts of.
from (Optional) : The date at which the chart should start from. See User.getWeeklyChartList for more.
to (Optional) : The date at which the chart should end on. See User.getWeeklyChartList for more.
api_key (Required) : A Last.fm API key.
I’ll call this method the same way I called user.getTopArtists
above:
params = {
'method': 'user.getweeklyartistchart',
'user': 'jeffreyflorek',
'api_key': api_key,
'format': 'json',
}
response = requests.get(root_url, params=params)
if response.status_code == 200:
data = response.json()
else:
print(f"Request failed with status code {response.status_code}")
print(f"Reason: {response.reason}")
The format of the data returned by this is slightly different, but getting a basic list of artists is pretty much the same as with the other method:
for artist in data['weeklyartistchart']['artist']:
print((f"- [{artist['name']}]({artist["url"]})"))
The results seem the same, but I saw differences when testing the code earlier. If I had to guess, it’d be that the user.getTopArtists
method provides the top artists for the past seven days, and that user.getWeeklyArtistChart
provides the top artists a chart that Last.fm updates at some regular interval.
Conclusion
I now understand the hype around RESTful APIs! I spent more time formatting this than I did figuring out how to get a list of my top artists from Last.fm. I’m looking forward to the next parts of this project, especially since omg.lol seems like it’s really focused on making everything available via its API. This may change, but as of now, here is what I’m planning for the following parts of this project:
- Modifying my now page with the omg.lol API
- Integrating the data from Last.fm with omg.lol (making sure it’s in the correct format and only updating certain sections of my now page)
- Making it run periodically
That should get this to a bare minimum functionality. I’ve also been thinking of future features like pulling from more APIs or making the script more general, but those are for the future!