このままでいいか

アニメとか,プログラミングとか,研究とか,思ったことをだらだらと書く感じで

AnimeAPIをCommonLispから叩いてみた!

はろー,イベントの企画やらTOEICやら研究やらでテンテコ舞いな中でも,なぜか余裕があるような気がしてしまうあぶです.

 

さて,はてなブログでブログを始めてから,ちゃんとプログラム書いたって話をしてませんでしたし,今回機会があったので,いっちょ書こうと思いまっす!

 #やること

することはタイトルの通りです.

アニメのデータをまとめて取得できるAPIがあるみたいなので,勉強がてら,CommonLispで叩いてみます.

 

#AnimeAPIの紹介

今回利用するAPIはProject Shangrilaというプロジェクトのものです.

 

 

因みに これ(↓)

github.com

です.

APIはREST形式で公開されているので,HTTPクライアントを使ってGETで叩けばJSONが返ってくるので,とても簡単に使うことができますねぇ!

 

いい感じで使えればこんな感じ(↓)でグラフ化とかできて嬉しい!みたいなAPIです.

f:id:dbym4820:20160504013151p:plain


この図は秋葉原IT戦略研究所ブログから拝借しました(勝手にすいません...)

 

まぁ,今回はとりあえず練習ということで,特定の時期に対応したアニメの情報を取得するだけのライブラリを書いてみました!(というか今回はタイトルを取得するだけなのでライブラリとも呼べないかも)

 

一応,ここ(↓)

github.com

にあげてます.

 

#実行環境

今回のテスト環境です.

言語:CommonLisp

処理系:Clozure CL 1.10

OS:Mac OS X 10.11.4(El Capitan)



#使用するライブラリ(Quicklisp)

HTTPクライアント:drakma

文字コードパーサ:babel

JSONパーサ:cl-json

 

#ライブラリのディレクトリ構造

 root/

 ├ scr/

   │     └ sola.lisp

 ├ package.lisp

   └sola.l.asd

ということで作ってます.

 

#本体の中身

一応ASDFライブラリってことでパッケ管理とか,依存ライブラリとかあるのですが,正直めんどくさいんで本体の簡単な説明だけにしときますね\(^ ^)/

 

以下のコードが本体です

sola.lisp

(in-package #:sola)

;;@param (int) year => アニメマスタを取得する年
;;@param (int) cours => アニメマスタを取得する期(1-4)
;;@result (string) anime-master-json => JSON形式のアニメマスタを文字列型で返す
(defun get-master-text-data (year cours)
  (let* ((api-url "http://api.moemoe.tokyo/anime/v1/master/")
     (anime-master-json (babel:octets-to-string
                 (drakma:http-request
                  (concatenate 'string api-url (princ-to-string year) "/" (princ-to-string cours)))
                 :encoding :utf-8)))
    anime-master-json))


;;JSONテキストを連想リストに変換するマクロ
(defmacro json-to-alist (json)
  (let ((jsons `(cl-json:decode-json-from-string ,json)))
    jsons))

;;@param (int) year => アニメマスタを取得する年
;;@param (int) cours => アニメマスタを取得する期(1-4)
;;@result (alist) anime-master-json => アニメマスタを連想リストで返す
(defun get-master-data (year cours)
  (let ((anime-master-alist (json-to-alist (get-master-text-data year cours))))
    anime-master-alist))

;;前から数番目の一つのアニメの関連情報を取得する
(defmacro get-one-anime-info (year cours num)
  `(nth ,num (get-master-data ,year ,cours)))
   
;;特定のアニメの関連情報(alist形式)の中からkeyに当てはまるタプルを取得する
(defmacro get-tuple-from-key (year cours key num)
  `(cdr (assoc ,key (get-one-anime-info ,year ,cours ,num))))


;;指定した期のアニメのタイトルを列挙する
;;とりあえず10000個以上も1期でアニメはやらないだろうから限界値を高く設定してるけど,さすがに後々賢くしよう
(defun get-titles (year cours)
  (loop for i below 100000
    do (if (get-tuple-from-key year cours :title i)
           (format t "~A~%" (get-tuple-from-key year cours :title i))
           (return))))

#各コードの解説

 

(defun get-master-text-data (year cours)
  (let* ((api-url "http://api.moemoe.tokyo/anime/v1/master/")
     (anime-master-json (babel:octets-to-string
                 (drakma:http-request
                  (concatenate 'string api-url (princ-to-string year) "/" (princ-to-string cours)))
                 :encoding :utf-8)))
    anime-master-json))

ここがメイン中のメインですね.

drakma(HTTPクライアント)でアニメAPIのURLにリクエストを投げてJSONファイルを受け取る.

ただし,drakmaで受け取る値は文字コードなので,それをbabelで変換したものを変数に束縛しておきます.

あと,使いやすさのために引数となる,年(year)と期(cours)は数値で入力できるように,「数値→文字列」変換を行ってます.

 

 

返値はJSON形式の文字列で,様々なデータが入ってますね.長いんで省略します.

 

 

(defmacro json-to-alist (json)
  (let ((jsons `(cl-json:decode-json-from-string ,json)))
    jsons))
(defun get-master-data (year cours)
  (let ((anime-master-alist (json-to-alist (get-master-text-data year cours))))
    anime-master-alist))

この部分でJSONデータを,CommonLispでわかりやすいように連想リストに変換します.

 

(defmacro get-one-anime-info (year cours num)
  `(nth ,num (get-master-data ,year ,cours)))
(defmacro get-tuple-from-key (year cours key num)
  `(cdr (assoc ,key (get-one-anime-info ,year ,cours ,num))))

この辺りまとめるべき処理なんでしょうがとりあえず即興ってことで,これから勉強して直していきます(^ ^;)

処理としては連想リストから特定のキーにあったタプルを取得します.

そして次が最終段階の「タイトルを取得する」部分ですね!

 

(defun get-titles (year cours)
  (loop for i below 100000
    do (if (get-tuple-from-key year cours :title i)
           (format t "~A~%" (get-tuple-from-key year cours :title i))
           (return))))

年と期に対応するタイトルを順番に取得していきます.

これも実装が適当で,ありえないくらい大きい数字を宣言しておき,「それ以上アニメのタイトルの情報はないよ!」って言われたら表示を終わるっていう仕掛けになってます.

 

プログラム書く人間としては恥ずかしい感じの実装かもしれませんがご容赦ください...

 

以上が簡単なプログラムの実装です.

 

#使ってみる

使い方はウルトラ簡単です.
マイライブラリをロードして,関数適用するだけ.
具体的には,2016年の1期目のアニメ一覧を見たければ

(ql:quickload :sola)
(sola:get-titles 2016 1)

と入力すれば

機動戦士ガンダム サンダーボルト
プリンス・オブ・ストライド オルタナティブ
無彩限のファントム・ワールド
ハルチカ〜ハルタとチカは青春する〜
ノルン+ノネット
アクティヴレイド -機動強襲室第八係-
少女たちは荒野を目指す
僕だけがいない街
おじさんとマシュマロ
ファンタシースターオンライン2 ジ アニメーション
だがしかし
暗殺教室(第2期)
ディバインゲート
おしえて!ギャル子ちゃん
石膏ボーイズ
霊剣山 星屑たちの宴
GATE 自衛隊 彼の地にて、斯く戦えり(2期)
昭和元禄落語心中
紅殻のパンドラ
ブブキ・ブランキ
ラクエンロジック
デュラララ!!×2
ナースウィッチ小麦ちゃんR
虹色デイズ
大家さんは思春期!
Dimension W
灰と幻想のグリムガル
シュヴァルツェスマーケン
最弱無敗の神装機竜(バハムート)
赤髪の白雪姫(第2期)
てーきゅう(第7期)
魔法少女なんてもういいですから。
蒼の彼方のフォーリズム
この素晴らしい世界に祝福を!
亜人
FAIRY TAIL ZERO
ももくり
この男子、魔法がお仕事です。
SUSHI POLICE
血液型くん!4

こんな感じでずらっと出てきます.

#実行画面

Emacs+slime REPLで実行した結果はこんな感じです.

f:id:dbym4820:20160504015934p:plain

 

#終わりに

今回はとりあえず即興で書いてみたのでgdgdコードでしたし,単純な情報の取得しかしていないので,次回のエントリでは可視化までした記事を書きたいと思います.

 

拙い実装ですので,大喜利大会のごとく突っ込んでもらえればいいかなぁ,ってカンジです!

 

今後はこのAPIと,Twitterとか検索履歴とかのいろんな情報を組み合わせて,LODとか機械学習とかエキスパートシステムとか使って,各ユーザが求めるものをドンピシャで提供できるようにしたいですね!

 

では,また!