ただの風邪。

音楽のことを中心にいろいろと書いています。

■カテゴリ別ショートカット

レビュー(音楽) レビュー(本) エッセイ、考えごと

Genius APIから特定のアーティストの楽曲一覧及び各メタデータを取得する(with Python)

はじめに

 RapGeniusとして開始した歌詞共有サイトGenius.comは、ラップ・ミュージックのリリックのみならずアノテーション(注釈)を共有しようとするヘッズたちの人気を集め、いつしかラップ以外のあらゆる音楽、いやあらゆるコンテンツ――動画、スピーチ、ウェブサイトなど――をカヴァーする一大ナレッジコミュニティとなった。Geniusは自分たちの持つリソースを活用してもらうため、技術者向けにAPIを公開している。ソフトウェアの開発者にはもちろん、楽曲のメタデータやアーティストのバイオグラフィ、そしてユーザーたちによるアノテーションにまで至る大量のデータベースは、歌詞そのものにアクセスすることはできないとはいえ、音楽やそれにまつわる言説について計量的に分析しようという人間にはもってこいの材料となる。

genius.com

 というわけで、ものは試し、Genius APIから特定のアーティストの楽曲一覧を取得し、さらに各々の楽曲のメタデータを含めたちょっとしたデータベースをつくってみることにした。

各種ドキュメンテーションなど

 まず必要になるのはAPIを利用するためのトークンだが、技術者向けウェブサイトトップの「CREATE AN API CLIENT」から作成することができる。APP NAMEとAPP WEBSITE URLさえ埋めればOKっぽいので、適当な名前と適切なURLを決めておく。

 APIの仕様(取得できるデータの形式やエンドポイント)はGeniusが提供するドキュメントに詳しく、気合で読んでけばなんとかなるやろう、という精神で行く。

docs.genius.com

 ここで仕様がわかればあとは各々好きな言語で好きなリクエストを送り、好きなデータを取得すればオッケー😆なのだけど、意外と実例が見つからなくて手間取ってしまった。参考になった2つのプロジェクトを以下に挙げておく。どちらもPythonを用いている。

bigishdata.com

www.jw.pe

 奇しくも両者の関心はリリックにあって、大部分はAPIを介さないウェブスクレイピングについての記述になっているけれど、今回利用したいのは楽曲の各種メタデータだ。PythonからGeniusのAPIを叩く方法と、データの扱い方だけ拝借する。

コード例1:基本的なリクエス

import requests, json

# constant values.
BASE_URL = "https://api.genius.com"
CLIENT_ACCESS_TOKEN = "<YOUR TOKEN HERE>"

# send request and get response in json format.
def _get(path, params=None, headers=None):

    # generate request URL
    requrl = '/'.join([BASE_URL, path])
    token = "Bearer {}".format(CLIENT_ACCESS_TOKEN)
    if headers:
        headers['Authorization'] = token
    else:
        headers = {"Authorization": token}

    response = requests.get(url=requrl, params=params, headers=headers)
    response.raise_for_status()

    return response.json()

 基本的には、エンドポイントのベースURLをあらかじめ指定して、_get関数の第一引数に渡したエンドポイントと組み合わせてリクエストURLをつくる。requests.get()で取得したデータはjson形式にしておく。あまりここらへんはよくわかってない。完全に雰囲気でやってる。

コード例2:アーティストIDの取得

# find artist id from given data.
find_id = _get("search", {'q': ARTIST_NAME})
for hit in find_id["response"]["hits"]:
   if hit["result"]["primary_artist"]["name"] == ARTIST_NAME:
       artist_id = hit["result"]["primary_artist"]["id"]
       break

 で、まずは調べたいアーティストのGenius内でのIDを取得する。アーティスト名で検索(エンドポイントはapi.genius.com/searchになる)をかけた結果のうち、フィーチャリングやプロデュースではなく自身の作品として(=primary_artistとして)登録されているエントリを探して、そこに併記されているidをゲットする。次はこのidからアーティストの楽曲(エンドポイントは/artists/<artist_id>/songs)一覧を入手する。ただし、アーティスト単位で楽曲を検索した場合、得られるメタデータはあんまり多くない。そこで、楽曲idだけを取得する。楽曲一覧の取得については前掲のJon Evansのコードをほとんどそのまま流用している。(ので割愛)

コード例3:曲のメタデータを取得

 さて、そうして楽曲idのリストが得られたわけだ。ここからは単純にsongエンドポイントにidを次々ぶち込んでいき、必要なデータを抜き出し、最終的になにかしらのファイルに保存することになる。ここでは、JSON形式で保存することを念頭に置いて、辞書型にデータを格納した。

def get_song_information(song_ids):
    # initialize a dictionary.
    song_list = {}

    # main loop
    for i, song_id in enumerate(song_ids):
        print("id:" + str(song_id) + " start. ->")

        path = "songs/{}".format(song_id)
        data = _get(path=path)["response"]["song"]

        song_list.update({
        i: {
            "title": data["title"],
            "album": data["album"]["name"] if data["album"] else "<single>",
            "release_date": data["release_date"] if data["release_date"] else "unidentified",
            "featured_artists":
                [feat["name"] if data["featured_artists"] else "" for feat in data["featured_artists"]],
            "producer_artists":
                [feat["name"] if data["producer_artists"] else "" for feat in data["producer_artists"]],
            "writer_artists":
                [feat["name"] if data["writer_artists"] else "" for feat in data["writer_artists"]],
            "genius_track_id": song_id,
            "genius_album_id": data["album"]["id"] if data["album"] else "none"}
        })

        print("-> id:" + str(song_id) + " is finished. \n")
    return song_list

 曲名はともかくとして他の項目はたまにnullというかNoneだったりするので、三項演算子で豪快にデータの存在を確認のうえ、各項目を追加していっている。これでいいのかわからないが、とりあえず動いたのでオッケーです。(6月22日追記 格納するデータ構造を見直し。ついでに、わけわからん判定をしていた三項演算子を若干スリム化。)

 そういうわけで、最終的にはこの辞書型のデータをJSONに書き込んで終わりです。

full_list_of_songs = get_song_information(song_ids)

with open("./" + ARTIST_NAME + " songs.json", "w") as f:
    json.dump(full_list_of_songs, f)

 量が膨大になったんで頭の数個しか確認してませんが、うまいこといってるようです。恐らく他のアーティスト名でもだいたい動くと思います。

地味な問題

 これ、曲のメタデータ中にアルバムの中でのトラックナンバーが入ってないっぽいんすよね…… つくったデータに何かしらの方法で情報を追加する必要がある。

f:id:tortoisetaughtus:20170621234633j:plain

 割とがんばってコード書いたので、pythonがんばえ~(;_;)ってなってた。マジで。最後にコードをフルで貼っときます。

gista60247b59ff1881fa4bb8846a9b44c96