ニコニコ動画に動画検索APIができたらしいので取り急ぎScalaで
割とどうでもいい前置き(読まなくてもよい)
ニコニコ動画の公開APIが発表されてはや数年、「いずれドキュメント化したい」みたいな話が運営からありつつ一向に音沙汰がないまま時間だけが過ぎてきました。(外部プレーヤーや各種APIの開放について)
その間ニコ生やらなんやらが流行ったりなんかして、いろんなところで解析なんかが行われたりなんかしてニコニコのサービスを外部から利用する手段はたくさん流通しているものの個人的には「なんかハックな感じでいやだなー」という印象でした。
ハックな感じで嫌だ、っていうのは何かものを作って公開するにあたって非公式なAPIだと本家の仕様に振り回されるし、情報は錯綜するしで利用する側としては居心地が良くないという意味です。そこら辺を気にしないのがマッシュアップであったり最近の考え方であったりするとすれば自分は古い人間なんでしょう。とりあえず「tcpdumpとか嫌だよう、めんどうだよう、検索する度にスクレイピングとかHTMLパースとか重いよう、ださいよう、仕様が変わって緊急リリースとか嫌だよう」というのが本音です。
そんな言い訳をしながら、楽しそうなニコニコサービスのハックを敬遠していたわけですが、思うところがあって(半ニートになって)ニコニコをいじるようになってちょっとハックってみようかなという気になっていました。2月ごろに取り組んでいてやっぱ「検索のたびにHTMLパースとかなぁ・・・、エージ構成変わったらアウトだしなぁ・・・」と思っていました。
で、5月に入ってZeroがリリースされたわけですが、プレイヤー画面で動画検索の結果がAjax的に表示されるようになりました。・・・これAPIあるよね?ってことで調べてみた次第。ちなみに気づいたのが6月15日くらい。目端が効かない人間は頭の回転も遅いのです。
APIの簡単な仕様
まずはこちらをごらんください。
http://ext.nicovideo.jp/api/search/search/minecraft?mode=watch&order=d&page=1&sort=n
はい。ニコニコ動画にログインしていればなんかたくさん表示されたと思います。ログインしていないと一行くらいが表示されたと思います。
上のURLの書式でHTTP GETすればJSONが返ってくるという次第です。
ね、簡単でしょ?で、実はこれ以上調べてなかったり。近いうちに整理します。
Queryについてはorderとsort、pageはニコニコ動画の検索のqueryと同様だと思う。modeが何を意味するのかはちょっとまだわからない。
上の検索はキーワード検索。タグ検索だとURLが変わる。
http://ext.nicovideo.jp/api/search/tag/minecraft?mode=watch&order=d&page=1&sort=n
じゃあScala(+dispatch)で
とにもかくにもコードを書こう。環境はScala 2.9.1 + databinder dispatch 0.8.8。
とりあえずログインして、とりあえず検索をかけて、とりあえずダンプするプログラム。
ひどいコードであるという批判は認める。
import scala.util.parsing.json._
import dispatch._
import org.apache.http.{HttpRequest, HttpResponse}
import org.apache.http.protocol.HttpContext
import org.apache.http.HttpStatus._
class NicoVideo
{
def query_test() =
{
// dispatchのチュートリアルとの違いその1
// これを行わないと302なリダイレクトなページにアクセスした際にリダイレクト先のコンテンツを返す
val h = new Http
{
override def make_client =
{
new ConfiguredHttpClient( new Http.CurrentCredentials(None) )
{
setRedirectStrategy(
new org.apache.http.impl.client.DefaultRedirectStrategy
{
override def isRedirected(
req: HttpRequest, res: HttpResponse, ctx: HttpContext) = false
})
}
}
}
// ログイン処理のリクエスト生成、メイルアドレスとパスワードをセットしてhttpsでPOST
// application/x-www-form-urlencodedを指定してあげないと正しく結果を返してくれない
val req =
(:/("secure.nicovideo.jp", 443) / "secure/login?site=niconico").secure <<
("mail=mail@mail.com.net&password=password",
"application/x-www-form-urlencoded")
// POSTを実行、とりあえずヘッダを表示する。クッキーがもらえれば成功
// dispatchのチュートリアルとの違いその2
// applyによる呼び出しだと302で例外が発生する。
// xかwhenメソッドを使う必要がある(whenは試してない)。
h.x( req
>:> { (headers) => headers foreach println }
)
// 続いて検索のリクエストを生成
val query =
(:/("ext.nicovideo.jp") /
"api/search/search/minecraft?mode=watch&order=d&page=1&sort=n")
// httpで投げつつJSONをパースしつつMapに変換、実にScalaらしい(?)
// Httpオブジェクトはクッキーを保持してくれている(らしい)。
val map =
JSON.parseFull( h.x( query >- ((res) => res ) ) ).get.asInstanceOf[
Map[String,Any]]
// とりあえず全部表示してみる
println(map)
// タイトルを抜き出してみる(コードが汚いです。整理の余地あり)
map.get("list").get.asInstanceOf[ List[Map[String,Any]] ].foreach (
(i:Map[String,Any]) =>
{
println(i.get("title").get)
})
}
}
object test
{
def main(args: Array[String])
{
val nico = new NicoVideo
nico query_test
}
}
今後
サーバサイドで使うなら検索用のアカウントを作るのがよいっぽい。Ajax的な場合はAccess-Control-Allow-Originヘッダがどうなるか調べないといけない。リクエスト自体はユーザが行うのでアカウントは問題ないと思う。スタンドアロンアプリケーションについてはどうなんだろうねというところ。
ニコニコのJavaScriptを眺めているとAPIやらAPI Keyやらの識別子があるので、近いうちにいろいろ整備されるかもしれない。ってか整備してほしい。
個人的にはTwitterのOAuthな感じでアカウント情報を使えるとうれしい。
ニコニコ動画の検索機能を探していたところ、このサイトに行き着きました。
ニコニコ側でAPIが用意されているとはびっくりでした。
おかげで実装できました。
ありがとうございます。
[...] 検索APIについては ニコニコ動画に動画検索APIができたらしいので取り急ぎScalaで [...]