ニコニコ動画からの情報取得用ライブラリ、作ってます。
Railsも初歩的な部分を理解し、あとは日々ニコ動から情報を取得すればいいだけなんですが、DB設計が思いの外難しく、あと一歩の所で完成を見ていません。
ただ、実用に足りる最低限のクローラはすでに完成しています。今日はそのクローラに使うRubyの自作ライブラリを紹介します。
NicoScraper
執筆時のバージョンは、0.2.12 です。
簡単に説明すると、動画、マイリスト、タグ検索結果の情報を取得し、取得結果からさらにいろいろ情報を抽出できるライブラリです。
詳しいことはドキュメントを読んでいただくとして、簡単な概要を。
インストール
libxml2とlibxsltを使うので、入ってない場合は、
yum install -y libxml2 libxml2-devel libxslt libxslt-devel
などとして入れて下さい。その後、
gem install nicoscraper
でインストールし、
require 'nicoscraper'
で準備完了。
使い方
例えば、「【初音ミク】みくみくにしてあげる♪【してやんよ】」の情報が欲しい時は、
require 'nicoscraper' movie = Nicos::Movie::new("sm1097445") movie.getInfo
このように、動画IDを与えてMovieクラスのインスタンスを作った後にgetInfoメソッドを使うと、
p movie <Nicos::Movie:0x00000002537aa8 @video_id="sm1097445", @available=true, @title="【初音ミク】みくみくにしてあげる♪【してやんよ】", @description="おまえら、みっくみくにしてやんよ。歌詞は...", @thumbnail_url="http://tn-skr2.smile...", @first_retrieve=1190218922, @length=99, @movie_type="flv", @size_high=3906547, @size_low=1688098, @view_counter=9073614, @comment_num=2553366, @mylist_counter=183470, @last_res_body="★███████████☆...", @watch_url="http://www.nicovideo.jp/...", @thumb_type="video", @embeddable=1, @no_live_play=0, @tags_jp=["音楽", "初音ミク", ...], @tags_tw=["彈幕強大", "把你MikuMiku掉♪", ...], @user_id=70391 >
というように、インスタンス変数の形で情報が取得できます。マイリストも同じように、
require 'nicoscraper' mylist = Nicos::Mylist::new("15196568") mylist.getInfo
マイリストIDを与えてgetInfoすると、
p mylist <Nicos::Mylist:0x00000002884670 @mylist_id=15196568, @available=true, @title="【Oblivion】おっさんの大冒険", ... @movies=[ #<Nicos::Movie:0x0000000255a968 @video_id="sm8481759", @available=true, @title="【Oblivion】おっさんの大冒険1(ゆっくり実況)", ... , #<Nicos::Movie:0x0000000251a6b0 @video_id="sm8506034", @available=true, @title="【Oblivion】おっさんの大冒険2(ゆっくり実況)", ... , ], ... >
マイリストの情報と、マイリストに含まれる動画情報が取得できます。
Searcherモジュール
このライブラリの肝、検索結果からの取得です。タグ検索を行う場合は、Nicos::Searcher::ByTagクラスを使います。
searchByTag = Nicos::Searcher::ByTag.new() searchByTag.execute( 'VOCALOID', :post_new ) { |result, status| # "continue"をブロック内で返すと検索を継続 }
このメソッドには、検索したいタグ文字列とソート方法を与えます。この例では、'VOCALOID'のタグを検索し、それを:post_new == 新着順でソートするという設定です。
そして、resultに取得結果がMovieクラスのインスタンスの形で返ってきます。statusには取得の成否等の情報が含まれますが、ここでは割愛します。
最後に、ブロック内で"continue"を返すことにより、検索が継続されます。これは意図しない過剰アクセス等を防ぐ為です。なお、タグ検索結果ページ/Atomを利用しているので、スクレイプは動画32個単位で行われます。
実践的な例
require 'nicoscraper' require 'date' t = Time.now tda = Date::new(t.year, t.month, t.day) - 3 threeDaysAgo = Time.local(tda.year, tda.month, tda.day, 0, 0, 0).to_i searchByTag = Nicos::Searcher::ByTag.new() searchByTag.execute( 'VOCALOID', :post_new ) { |result, status| terminate = false result.each { |movie| # first_retrieve == 投稿日 terminate = ( movie.first_retrieve <= threeDaysAgo ) puts movie.title + " is posted at " + Time.at(movie.first_retrieve).to_s } "continue" unless terminate }
私はこんな感じで自分のクローラに利用しています。この例では、今日より3日前の0時までを限度に、VOCALOIDタグの付く新着動画を取得します。ブロック内に与えられた動画インスタンスの投稿日を見て、3日以上前の物が含まれていたら”continue”を返すのをやめるという仕組みです。
シリーズ性判定メソッド
動画には説明文があり、多くの動画はマイリスト情報をそこに記載し、他の自作/他作動画を紹介しています。そしてシリーズ物の動画の場合は、そのシリーズ一覧を記載したマイリスを用意する場合があります。そんなシリーズマイリスを抽出するsearchSeriesMlメソッドを用意しました。
例えばこのシリーズ、私は大好きなんですが、part1動画の説明文を見るとマイリストが2つありますよね。自分を含むシリーズ物をまとめたマイリスと、その他いろいろのマイリスです。
シリーズ性判定メソッドは、ある動画の説明文からマイリス文字列を抽出し、1. そのマイリスに対して自分自身が含まれているかどうかを判定し、2. 含まれている場合は動画のタイトルの類似性を計算して返します。類似性はマイリス内動画のタイトルをngram法(デフォルトはn=3)によって総当たりで比較し、切り出した文字片が合致する割合の平均を用います。
そうすると、例えば先の動画の場合、類似度がそれぞれ、
- 自分を含むシリーズ物をまとめたマイリス: 0.82
- その他いろいろのマイリス: 0.07
と計算されます。どちらも自分自身を含むマイリスなので、ここから先は人の判断に委ねられますが、少なくとも類似度が最も高いものがシリーズ物マイリスであると推測できますし、このシリーズでは現にそうなっています。n=3の場合、大体0.2-0.3ぐらいにしきい値を設けるのが、経験的には妥当だと言えます。
実際にはこんなふうに使います。
# 引数にしきい値を設定し、それ以下の類似性のマイリストを出力から排除できる。 ary = movie.searchSeriesMl(0.1) ary.each do |e| puts "title:" + e[:mylistObj].title puts "similarity: " + e[:similarity] end
このメソッドは、該当するマイリストオブジェクトとその類似性を含むハッシュオブジェクトの配列を返します。詳しく書くと長くなってしまうので、興味があればこれもドキュメントを読んで下さい。
他には?
他にはスクレイプ時の動画やマイリストの状態ー404、コミュニティ限定、非公開を認識できたり、連続アクセス制限や503等に応じて次回以降のウェイトを自動で増加させたり再試行回数等を調整できたり、外国語タグやカテゴリタグも認識できたりとか、いろいろ伝えたいことはあるんですが、長くなるのでやめます。詳しくはドキュメントを。
最後にお願い
ニコニコ動画は規約でクローリング自体を許可とも禁止とも言っていません。が、当然ながらサーバに過度の負担を及ぼす行為はやめろと言っています。このライブラリの使い方によっては、それに該当する可能性があります。
その対策として、'continue'を明示的に返さないとスクレイプが継続しない方式の採用と、デフォルトのウェイトをかなり大きめに取る(ウェイトについてはこちらを見てください)という対策を行なっています。したがって、実用的な環境にするためには皆さんの手で変更を加えて頂く必要がありますが、その際は十分注意して下さい。例えばブロック内に制御構文なしに無条件で'continue'を返したりすると、タグによっては大変な事になります。このライブラリを使って生じた損害、例えばニコ動からのアクセス規制、垢バン、損害賠償などについて、一切の保障はできません。つまり、MITライセンス通りです。
あと、バグが多くてパッチバージョンがガンガン上がっています。私は限定的な使い方しかしないので、全てのメソッドのテストを十分に行えていないせいです。バグを発見したら、下記まで連絡をいただけると助かります。要望があれば、それも是非聞かせて下さい。
twitter: @h_demon
mail: hdemon7@gmail.com