SoundCloud APIと戦いLikesをすべて取得する
普段音楽をSoundCloudで聞いているわけなんですが、これのLikesに登録した曲たちのURLが全部わかるとうれしいなということなのでそれをやります。
やりたいこと
大きくは2つあり、
- 自分のLikesの数の取得
- すべてのLikesのURLを得る
です。
ここからは割と苦労話が入るので手法だけ知りてーよという場合は下まで飛ばしてまとめをご確認ください。
Likes数の取得
SoundCloudはAPIを公開しています。
developers.soundcloud.com
で、これを見るにLikeを取りたいだけならAuthする必要はないので簡単じゃ~んとか思っていると一番最初に困るのはClientIDだと思われます。
こいつはSoundCloudのAppを新しく作るとそいつに対して割り当てられるのですが、なんとSoundCloudは今現在新しいAppの登録を凍結しています。
なので頑張って各自どうにかしましょう*1。
ClientIDはまぁ得たものとして話を進めると、次に困るのはUserIDでしょう。
これはSoundCloudに登録している各ユーザーに対して一意なIDなわけですが、パッと得ることができません。
おそらく最も行儀の良い方法は、
- SoundCloudにログイン
- Settingsに移動
- Contentタブに移動
- RSS FeedのURLの中のsoundcloud:users: 以下の数字を確認
だと思います。
私は行儀が悪いのでログインした後トップページのコード内のJSを見てapi.soundcloud.com/users/以下を確認して得ました。まぁ数字が分かればなんでも良いです。
で、これが分かると
https://api.soundcloud.com/users/{UserID}?client_id=YOUR_CLIENT_ID
にアクセスするだけでとりあえずユーザー情報がJSONで得られます。
この中にpublic_favorite_countがあるのでそれにアクセスすればLikesが得られます!
……という幸せな終わりを目指していたのですが、これによって得られるLikesの数はどうもサイト上のLikesの数と一致しません。勝手な推測ですが、おそらくLikeしたあとでその曲が消えた場合、ブラウザ上では反映される一方このAPIには反映されていないのではと思っています。
ここで、そもそもサイト上ではこのAPIを使っているはずでそれとこれが一致しないのは何故なのか?という疑問が生まれます。
で、確認するためSoundCloudのトラフィックを見たところ、なんと今SoundCloudはこのAPIを使っていないらしく、アクセスしているのは
api-v2.soundcloud.com
でした。
そしてこのAPI-V2はUndocumentedです。つらい。
とりあえず調べた結果、
https://api-v2.soundcloud.com/users/{UserID}?client_id=YOUR_CLIENT_ID
でJSONが得られ、その中にlikes_countがあるのでそれを得ればOKです。これはサイト上の情報と一致します。
全てのLikeした曲のURLを取得
とりあえずAPI-V2の存在は一旦忘れ、Documentedな方法でURLを得ようと考えました。
ドキュメントいわく、Likes一覧は
https://api.soundcloud.com/users/{UserID}/favorites?client_id=YOUR_CLIENT_ID
にGETを投げつければJSONで得られます。
が、こいつはそれほど多くの曲リストを同時に返してくれません。
私はLikesが500を超えていて、それを一気には返してくれないということがわかりました(負荷を考えればそれはそう)
ちょっとググったところ、?limit=と?offset=というパラメータが有効っぽい記事を見つけ、なるほどねということで
https://api.soundcloud.com/users/{UserID}/favorites?limit=200&offset=300&client_id=YOUR_CLIENT_ID
みたいにアクセスするとこれは死にました。
これについてもう少し調べたところ、どうやら数年前にoffsetパラメータは殺されてしまったらしく、offset=200までしか動かないようです。
そして現在はlinked_partitioning=1を追加すると帰ってくるJSONの中にnext_hrefという要素があるらしく、これにURLが入っているのでそれを使ってページングするらしいです。なるほどね。
なので、結局アクセスするのは
https://api.soundcloud.com/users/{UserID}/favorites?limit=200&linked_partitioning=1&client_id=YOUR_CLIENT_ID
で、next_hrefがある限り繰り返しアクセスすることで全部得る という感じになりました。
そしてここでまた変なことが起き、これで得たURLの数がapi-v1で得たfavoriteの数ともapi-v2で得た数とも一致しませんでした。俺は一体何を信じれば良いんだ。
概ねやりたいことができたのでここで力尽きました。これに関して有用な情報(あるいはapi-v2で試した結果)がある場合ぜひ教えていただきたいです。
まとめ
Likesの取得
そのままコピペしても動かないので注意(UserIDとかClientIDとか)
http = urllib3.PoolManager( cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) profile_url = "https://api-v2.soundcloud.com/users/{UserID}?client_id={ClientID}" source = http.request('GET', profile_url) raw = source.data.decode('utf-8') pyson = json.loads(raw) print(pyson["likes_count"])
全てのLikesのURLの取得
http = urllib3.PoolManager( cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) url = "https://api.soundcloud.com/users/"+ str(userID) + "/favorites?limit=" + str(limit) + "&linked_partitioning=1&client_id=str(CID)" remoteURLList = [] while repeat: source = http.request('GET', url) raw = source.data.decode('utf-8') pyson = json.loads(raw) for urlData in pyson["collection"]: remoteURLList.append(urlData['permalink_url']) if "next_href" in pyson: url = pyson["next_href"] else: break print(remoteURLList)
*1:これは全く関係ない話なのですが、Youtube-dlやscdlなどのSoundCloud対応のダウンロードサービスのいくつかはgithubで公開されています。そしてこれらは内部でSoundCloudのAPIにアクセスする必要があります