wtorek, 28 lutego 2012

Szukamy ciekawych zespołów muzycznych za pomocą Last.fm API

Last.fm jest popularnym portalem muzycznym. Oferuje on również bogate API pozwalające wykorzystać bazę portalu w naszych własnych aplikacjach. Skorzystamy z tego aby zbudować prosty system rekomendujący artystów do posłuchania.


Aby zbudować taki system należy:
  1. Założyć konto w serwisie last.fm oraz zgłosić swój chęć skorzystania z API
  2. Sprawdzić format otrzymywanych danych
  3. Przygotować lokalną aplikację pokazującą rekomendacje
  4. Przenieść aplikacji w online 
A na koniec wspomnę o ograniczeniach i tym jak sobie z nimi radzić.



Założenie konta na portalu last.fm

Aby móc skorzystać z API musimy posiadać konto w serwisie oraz po jego założeniu zgłosić chęć korzystania z API. Zostanie dla nas wówczas wygenerowany API klucz, który potrzebny będzie do wywołania funkcji.
Oprócz API key otrzymamy również secret key. Jest on potrzebny do wywoływania funkcji wymagających autentykacji. Jednak ponieważ funkcją którą potrzebujemy do pobrania rekomendacji nie wymaga bycia zalogowanym, to klucz ten nie będzie nam w tym przykładzie potrzebny.

W razie kłopotów ze znalezieniem miejsca uzyskania klucza, wybierzcie z dolnej części strony opcję API.




Sprawdzamy format otrzymywanych danych

Skoro mamy już klucz API, to pora sprawdzić pobieranie danych. Nas interesuje funkcja: artist.getSimilar. Spróbujmy poszukać coś z klasyki na przykład Pink Floyd.
W tym celu otwieramy naszą ulubioną przeglądarkę i wpisujemy:

http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=Pink+Floyd&api_key=<tu wpisz swój klucz>

Oczywiście zamiast <tu wpisz swój klucz> wpisujemy klucz API który uzyskaliśmy w serwisie.

Jeżeli wszystko poprawienie zrobiliśmy to powinniśmy zobaczyć XML-a z danymi wyglądającego podobnie do poniższego przykładu:

<lfm status="ok">
<similarartists artist="Pink Floyd">
<artist>
    <name>David Gilmour</name>
    <mbid>1dce970e-34bc-48b2-ab51-48d87544a4c2</mbid>
    <match>1</match>
    <url>www.last.fm/music/David+Gilmour</url>
    <image size="small">http://userserve-ak.last.fm/serve/34/796768.jpg</image>
    <streamable>1</streamable>
</artist> 
...


Przygotowujemy aplikację

Aplikację przygotujemy w pythonie. Jest to jeden z mniej znanych mi języków a przecież bloga tego założyłem by się czegoś nauczyć :-)

import urllib
import urllib2
from xml.dom import minidom


class Lastfm(object):

    API_URL = 'http://ws.audioscrobbler.com/2.0/?method=%s&%s&api_key=%s'
    
    def __init__(self, key):
        self.apiKey = key


    def getSimilarArtists(self, artist, minMath=0.4):
        dom = self._execute('artist.getsimilar', {'artist':artist})
        artists = []
        for node in dom.getElementsByTagName('artist'):
            matching = float(self._getText(node, 'match'))
            if matching >= minMath:
                artists.append({'name': self._getText(node, 'name'),
                                'url': self._getText(node, 'url')
                                })
            
        return artists


    def _execute(self, method, params):
        params['method'] = method
        params['api_key'] = self.apiKey
        values = urllib.urlencode(params)
        req = urllib2.Request(Lastfm.API_URL, values)
        response = urllib2.urlopen(req)
        return minidom.parseString(response.read())
    
    
    def _getText(self, element, childName):
        nodeList = element.getElementsByTagName(childName)[0].childNodes
        textFragments = []
        for node in nodeList:
            if node.nodeType == node.TEXT_NODE:
                textFragments.append(node.data)
        return ''.join(textFragments)

Teraz możemy sprawdzić jak to wszystko zadziała. W tym celu przechodzi w pythonie do trybu interaktywnego (proszę pamiętać o wstawieniu swojego klucza):


Python 2.7.2+ (default, Oct  4 2011, 20:03:08) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from webapi.lastfm import Lastfm
>>> api = Lastfm('b25b959554ed76058ac220b7b2e0a026')
>>> artists = api.getSimilarArtists('Audioslave')
>>> for artist in artists: print artist['name'], artist['url']
... 
Chris Cornell www.last.fm/music/Chris+Cornell
Soundgarden www.last.fm/music/Soundgarden
Temple of the Dog www.last.fm/music/Temple+of+the+Dog
Rage Against the Machine www.last.fm/music/Rage+Against+the+Machine
Foo Fighters www.last.fm/music/Foo+Fighters
>>>


Przeniesienie aplikacji w online

Skoro aplikację lokalną mamy w pythonie to wersja online będzie oczywiście w django :-)
Dla uproszczenia przykładu nie będziemy korzystali z bazy danych oraz klucz do API wpiszemy w kodzie. W wersji produkcyjnej nie powinniśmy tego robić, ale o tym w następnym paragrafie.

Zacznijmy od przygotowania formularza:

class ArtisNameForm(forms.Form):
    artist = forms.CharField(max_length=100)

Nie ma tu nic ciekawego. Potrzebujemy tylko jedno pole do wprowadzenia szukanego wykonwacy.
Teraz pora na szablon:

{% csrf_token %} {{ form.artist }}
    {% for artist in recommendations %}
  • {{artist.name}}
  • {% endfor %}


W szablonie pobieramy nazwę artysty i pokazujemy listę podobnych artystów, wraz z linkami do ich profilu.
Pozostał jeszcze widok:

def index(request):
    recommendations = []
    form = ArtistNameForm(request.GET)
    if form.is_valid():
        artistName = form.cleaned_data['artist']
        api = Lastfm('b25b959554ed76058ac220b7b2e0a026')
        recommendations = api.getSimilarArtists(artistName)

    return render_to_response('projekt1/index.html', 
                    {'form': form, 'recommendations': recommendations}) 

Efekt końcowy można obejrzeć poniżej poniżej:



Jak widać za pomocą kilku linijek kodu można uzyskać całkiem fajne efekty.
Chętnych zachęcam do modyfikacji programu tak by można było wprowadzić 2 artystów i pokazać te zespołu które są podobne do obu wprowadzonych wykonawców.
A tych z Was którzy chcieliby poszukać nowego wykonawcy zapraszam na stronę:
http://programowalnasiec.appspot.com/project/1/
gdzie jest wystawiana przykładowa opisana tu aplikacja.


Ograniczenia i jak sobie z nimi radzić

Mamy już działającą aplikację. Na potrzeby testów jest to całkowicie wystarczające. Jeżeli jednak mamy zamiar używać tej funkcjonalności produkcyjnie to powinniśmy wziąć pod uwagę następujące kwestie:
  1. API nie powinno być wpisane w kodzie
  2. Cache obiektów
  3. Wykorzystanie komercyjne

API nie powinno być wpisane w kodzie
Ponieważ jest to typowy parametr konfiguracyjny to mamy co najmniej 2 możliwości. 
  1. Zapisanie w pliku konfiguracyjnym
  2. Zapisanie w bazie danych
Pewnie najprościej będzie skorzystać z pliku konfiguracyjnego. Ja jednak ze względu na korzystnie z wielu kluczy zapisuję je w bazie danych w tabeli o strukturze:

class ApiKey(models.Model):
    api_provider = models.CharField(max_length=255)
    key = models.CharField(max_length=255)


Cache obiektów
Praktycznie każde dostępne w internecie API ma ograniczenia dotyczące ilości wywołań w określonym okresie czasu. Np. last.fm pozwala na maksymalnie 5 requestów na sekundę.
W przypadku większej ilości zapytań zostaniem chwilowo zablokowani. A jeżeli sytuacja będzie się powtarzała to grozi naszemu kluczowi permanentna blokada.
Rozwiązaniem tego problemu, zresztą zalecanym przez last.fm, jest cachowanie obiektów.

Ja w swoich projektach stosuję prostą tabelę wiążącą URL zapytania z ostatnio otrzymaną zawartością oraz datą ostatniej aktualizacji:

class ApiCache(models.Model):
    url = models.CharField(max_length=1024)
    last_updated = models.DateTimeField()
    content = models.TextField()



Wykorzystanie komercyjne
Z API last.fm można korzystać w przypadku projektów niekomercyjnych. Zresztą to dosyć typowa sytuacji. Jeżeli chcemy używać API w projektach, które są nastawione komercyjnie należy dokładnie przeczytać warunki umowy. I to przed rozpoczęciem projektu. Inaczej możemy zbudować serwis w oparciu o API, które nie będzie dla nas dostępne.



Brak komentarzy:

Prześlij komentarz