以下はrubydocからの転載です。詳細はそちらを御覧下さい。
NicoScraper
My site: http://hdemon.net
GitHub: http://github.com/hdemon/nicoscraper/
Author: Masami Yonehara
Copyright: 2011
License: MIT License
Latest Version: 0.2.15.2
Release Date: Nov 12th 2011
何をするライブラリなの?
ニコニコ動画の動画ページ、検索ページ、あるいはそのAtomフィードから情報を取得し、その情報に対して各種操作を行えます。タグやマイリスト検索結果からの抽出、および抽出結果に対する反復処理を行うメソッドも備え、ランキングサイト等の制作を支援します。
導入
- インストール
動作にはRuby 1.9.2 が必要です。インストールには、
$ gem install nicoscraper
として下さい。依存関係としてMechanizeとlibxml-rubyがインストールされます。
もしlibxml2
やlibxslt
が足りない等のエラーが出た場合には、それらを導入して下さい。yumならば、
yum install -y libxml2 libxml2-devel libxslt libxslt-devel
とすれば、だいたい解決すると思います。
使用法
require 'nicoscraper'
で使うことができます。なお、実行前に注意点、および免責事項をお読み下さい。
基本的な使い方
Movieクラス、Mylistクラス、Searcherモジュールから主要な機能が構成されています。基本的には、動画やマイリストのIDを指定してインスタンスを作り、そこから詳細な情報を取得するためのメソッドを実行したのち、さらに分析や加工を行う別のメソッドを実行する、という手順を踏みます。
動画情報の取得
例えばsm1097445
という動画IDから、タイトルや動画の長さ、現在の閲覧数等の詳細な情報を知りたいときは、
require 'nicoscraper'
movie = Nicos::Movie::new("sm1097445")
movie.getInfo
上の例のように、Movieクラスのインスタンス(以下「動画インスタンス」)を動画IDを与えて生成した後、getInfo
メソッドを利用します。その結果、
p movie
<Nicos::Movie:0x00000003590a80
@video_id="sm1097445",
@available=true,
@title="【初音ミク】みくみくにしてあげる♪【してやんよ】",
@description="おまえら、みっくみくにしてやんよ。歌詞はhttp://ikamo.hp.infoseek.co.jp/mikumiku.txt(9/20 1:55修正)。上げている他のもの→mylist/1450136",
@thumbnail_url="http://tn-skr2.smilevideo.jp/smile?i=1097445",
@first_retrieve=1190218922,
@length=99, @movie_type="flv",
@size_high=3906547,
@size_low=1688098,
@view_counter=9151941,
@comment_num=2568537,
@mylist_counter=184674,
@last_res_body="...",
@watch_url="http://www.nicovideo.jp/watch/sm1097445",
@thumb_type="video",
@embeddable=1,
@no_live_play=0,
@tags_jp=[
{"value"=>"音楽", :locked=>true, :category=>true},
{"value"=>"初音ミク", :locked=>true},
{"value"=>"みくみくにしてあげる♪", :locked=>true},
{"value"=>"ミクオリジナル曲"},
{"value"=>"ika"},
{"value"=>"VOCALOID殿堂入り"},
{"value"=>"元気が出るミクうた"},
{"value"=>"VOCALOID"},
{"value"=>"初音ミク名曲リンク"},
{"value"=>"夜みっく"}],
@tags_tw=[
{"value"=>"彈幕強大"},
{"value"=>"把你MikuMiku掉♪"},
{"value"=>"週刊vocaloid排行榜第1名獲得曲"},
{"value"=>"翻譯歌曲"},
{"value"=>"台灣VOCALOID評論人氣曲"},
{"value"=>"最強彈幕傳說"},
{"value"=>"3次第一的傳說"},
{"value"=>"已翻譯歌曲"}],
@user_id=70391
>
このように、動画インスタンスにインスタンス変数として各種情報が付加されます。
マイリスト情報の取得
Mylistクラスもほぼ同様ですが、Mylistクラスのインスタンス(以下「マイリストインスタンス」)は、マイリスト情報の他に、そのマイリストが含む動画のインスタンスを自動的に生成します。つまり、
require 'nicoscraper'
mylist = Nicos::Mylist::new("15196568")
mylist.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(ゆっくり実況)",
... ,
],
... >
というように、動画インスタンスを勝手につくりだして配列として保持します。もちろん、これらの動画インスタンスには独立した操作を加えられます。ただし、取得ソースが異なるため、MovieクラスのgetInfoメソッドおよびgetHtmlInfoメソッドと、全く同じ内容が取得できるわけではありません。
検索結果の取得
タグやマイリスト検索結果からの情報取得には、Searcherモジュールを使います。情報のソート方法の指定、取得する範囲の制限が可能です。
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をブロック内で返すと検索を継続
:continue unless terminate
}
この例では、VOCALOID
というタグの付く動画を、:post_new
=投稿日時が新しい順からさかのぼって取得し、取得した動画の日付が3日前の0時0分を超えるまでそれを続けます。
ブロック内の第1引数には取得結果に基づく動画インスタンスが与えられるのですが、これは32個分の配列です。なぜ32個のセットなのかと言うと、ご存知のようにニコニコ動画の検索画面はページで区切られており、Searcherモジュールの各メソッドはページ毎に情報を取得し、ページ単位でブロックをコールするからです。HTMLから取得するにしろAtomフィードから取得するにしろ、1ページに32個の動画情報が含まれています。第2引数にはそれまでのアクセス成否情報等が渡されます。
そして、ブロック内でシンボル:continue
(もしくは"continue"文字列)を返すことによりスクレイプが継続します。つまり、:continue
を返し続けるロジックを組み込まないと、1ページ目を読んだ時点で処理が終了します。これは意図せざる過剰アクセスを防ぐための措置です。
上の例では、取得した動画の日付を調べ、3日前の0時0分より前の動画に到達すればそこでループを終える設計です。ループを継続するために取得情報を使うかどうかは任意なので、例えば10分間の制限で取得出来るだけ取得するということも可能でしょう。
取得した情報に対する操作
現在のところ、以下のような操作が可能です。詳しい使い方は、各メソッドの説明を参照して下さい。
動画の説明文からタイトルを取得する。 Nicos::Movie::extrMylist
動画の説明文中に、mylist/...
という表記で投稿者がマイリストを提示している事があります。extrMylist
はこれを全て取得し、配列として返します。
指定したマイリストに、自分自身が入っているかを調べる。 Nicos::Movie::isBelongsTo
そのマイリスト内に含まれる全ての動画の、タイトルの類似性を調べる。 Nicos::Mylist::getSimilarity
マイリストのシリーズ性を判定するために、マイリスト内の全ての動画の組み合わせで、タイトルの「編集距離」に基づく類似度を計算します。
その動画が属する、シリーズとみなせるマイリストのIDを返します。 Nicos::Movie::SearchSeriesMl
isBelongsTo
とgetSimilarity
の組み合わせにより、ある動画の説明文中にマイリストの記載がある場合、そのマイリストがタイトルの類似性によるシリーズとみなせるならば、そのIDを返します。
ウェイト設定について
ウェイトの役割
Searcherメソッドは継続的なアクセスを行い、またそれ以外のメソッドも実際の運用目的上ある程度の連続使用が前提になると思います。このライブラリは並列的なリクエストを行いませんが、それでも過剰なアクセスに伴うサーバからの拒絶や、あるいはそれ以上に、アカウントの停止もしくは法的責任を追求されるなどの事があり得ないという保証はできません。
それを防ぐための措置の一つが、"continue"
を明示的に返さないとスクレイピングが継続しない仕様ですが、もう一つ、アクセス中のウェイトを任意に設定できるようにしています。具体的には、連続リクエストの上限回数、連続リクエスト後のウェイト、1リクエスト毎のウェイト、連続アクセス拒絶時やサーバ混雑時の再試行までのウェイトなどです。ウェイトの設定は、以下のようにNicos::Connector::Config::waitConfig
に与えられています。以下はデフォルトの設定です。
Nicos::Connector::Config::waitConfig = {
:seqAccLimit => 10, # 連続してリクエストする回数
:afterSeq => 10, # 連続リクエスト後のウェイト(以下全て単位は秒)
:each => 1, # 連続リクエスト時の、1リクエスト毎のウェイト
:increment => 1, # 例外発生時の、次回以降の1リクエスト毎のウェイトの増加量
:deniedSeqReq=> { # 連続アクセス拒絶時
:retryLimit => 3, # 再試行回数の上限
:wait => 120 # 再試行までのウェイト
},
:serverIsBusy=> { # サーバ混雑時
:retryLimit => 3,
:wait => 120
},
:serviceUnavailable => { # 503時
:retryLimit => 3,
:wait => 120
},
:timedOut => { # タイムアウト時
:retryLimit => 3,
:wait => 10
}
}
連続リクエストとは?
Searcherメソッドはある一定回数のHTTPリクエストを1つの単位とし、その単位のリクエストが終わるごとに休憩を入れます。この1単位を連続リクエストと言います。上の例では、10のリクエストを1単位とし(seqAccLimit
)、その連続リクエストが終わった後に10秒の休憩を入れる(afterSeq
)設定になっています。
なお、連続リクエスト毎に限らず、1リクエスト毎のウェイトも併せて設定できます。上の例では、1リクエスト毎に1秒のウェイトを入れる設定です(each
)。
レスポンスの種類に対する反応について
ニコニコ動画のサーバのレスポンスには、正常にデータを返す以外にいくつかの反応があります。この反応に応じて再試行するか、それともそのリクエストをパスするかが決定されます。以下はレスポンスの内容と、それに対応するウェイト設定用ハッシュのキー、およびメソッドが返すステータスです。ステータスについては各メソッドの説明を参照して下さい。
削除済み :deleted
404 :notFound
再試行をせず、次の処理に移行します。
非公開 :notPublic
コミュニティ限定 :limInCommunity
動画がマイリストが非公開設定されている場合、あるいはコミュニティ未加入者には非公開になっている動画があります。後者については(前者も自分のものであれば)ログイン処理を事前に行うことで技術的には取得可能ですが、v0.2では未実装です。これらの動画の場合、処理は404に準じます。なお、この場合は内部的には403が返ってきています。
連続アクセスの拒絶 :deniedSeqReq
"短時間での連続アクセスはご遠慮ください" と表示される場合です。設定に従って再試行します。
サーバ混雑時 :serverIsBusy
"大変ご迷惑をおかけいたしますが、しばらく時間をあけてから再度検索いただくようご協力をお願いいたします。" と表示される場合です。再試行します。
503 :serviceUnavailable
メンテナンス時に限らず、稼働時にも稀に発生します。処理全体を中断することはなく、再試行します。
タイムアウト :timedOut
再試行します。
再試行するメソッドの場合、ウェイトのincrement
の秒数、次回以降の1リクエスト毎のウェイトを増加させます。
上記のどれにも該当しない場合、つまり取得とパースに成功した場合には:succeed
を返します。なお、マイリストは有効に存在し取得もできるが、動画は存在しないという場合も:succeed
であり、@movies配列が空になるだけです。
設定方法
ウェイトは、全メソッドが共有する設定と、各インスタンスのみに有効な設定の2つを定義できます。
また、全メソッドの共有設定には初期設定があり、それが上に挙げたハッシュです。これを変更するには、Nicos::Connector::Config::setWait`メソッドを利用します。これにより、その後に生成した各インスタンスにおいて、変更した設定が共有されます。
もう1つの方法は、動画・マイリスト・Searcherモジュール下クラスの特異メソッドとしての`setWait'メソッドを使う方法です。この方法では、そのインスタンスにおいてのみ変更が有効になります。
なお、setWaitメソッドは指定したキーの部分のみを上書きするため、設定毎に上記の書式のハッシュオブジェクトを用意する必要はありません。
wait = {
:seqAccLimit => 100,
:deniedSeqReq => {
:wait => 1200
}
}
Nicos::Connector::Config::setWait(wait)
例えばこのようにすることで、次回以降のseqAccLimit
とdeniedSeqReq -> wait
のみが前回の設定に上書きされます。
注意点、および免責事項
繰り返しになりますが、それぞれのメソッドは大半がニコニコ動画へのアクセスを伴い、特にSearcherモジュールは継続的かつ無制限なアクセスを可能にするため、使用方法によっては開発者である私が通常の使用において想定していない負荷を、ニコニコ動画に対して与える可能性があります。
その結果、アカウントの停止や法的な責任を追求される可能性も無いとは言えません。その点を考慮し、上で述べた幾つかの制限を行なっています。特にウェイトは大きめに設定してあります。
このウェイトは、ご自分の責任において変更して下さい。本ライブラリの使用によって発生した損害および法的な責任については、開発者が現在認識していない、あるいはそのバージョンの公開時には認識していなかったバグに起因するものを含め、一切の責任を負いかねます。またこのような事情から、予告なく公開を停止する可能性があります。
なお、HTMLからスクレイプするメソッドよりも、Atomフィードを使うメソッドの方がニコニコ動画側の負荷が(たぶん)軽く、アクセス制限などは経験上起こりにくくなっています。HTMLメソッドを利用した場合、混雑時には結構な頻度で連続アクセスが拒絶されます。大半の情報はAtomフィードで取得できるため、そうでない情報を取得したい場合に限り、HTMLを利用するメソッドを使うべきでしょう。
その他
文中の用語・用法
動画インスタンス Movieクラスのインスタンス
マイリストインスタンス Mylistクラスのインスタンス
動画ID | video_id ニコニコ動画の各動画に与えられる、sm|nmで始まる一意のID。
アイテムID | item_id
動画に与えられるもう一つの一意なIDであり、投稿日時と同じか非常に近いUNIX時間になっている。例えば、"【初音ミク】みくみくにしてあげる♪【してやんよ】"の動画IDはsm1097445
であり、アイテムIDは1190218917
である。このアイテムIDを日時に直すと、日本時間における2007年9月20日 1:21:57となるが、動画に投稿日時として表示されるのは、2007年9月20日 1:22:02である。
要望、バグ報告について
以下のどちらかにお願いします。
GitHubを経由して下さってもいいのですが、まだ慣れていないので対応が遅れるかもしれません。
今後の予定
v 0.3
HTMLから取得・解析するメソッドの追加。
キーワード検索メソッドの実装。
コメント取得メソッドの実装。
v 0.4-
シリーズ性判定の強化。説明文中にある「次 sm***」等の表記を解析し、マイリストに頼らずにシリーズ性を判定するようにする。
コミュニティ動画、限定公開動画・マイリストへの対応。
更新履歴
*v 0.2.15 *
getMoreInfoメソッドを、getHtmlInfoメソッドへ改名。
タグ情報が適切に取得できないバグを解消。
タグ情報の構造を変更。個々のタグ情報を、ロックされているか、カテゴリタグであるかの識別プロパティを持ちうるハッシュにし、それを配列として保持する方式へ。
v 0.2.13 0.2.14
タイトル類似性判定ロジックを、編集距離う使う手法からn-gram法(n=3)に変更。
htmlを用いたより詳細な情報を取得するMylist::getMoreInfoメソッドを試験的に実装。
v 0.2.11 0.2.12
- Seacherの各メソッドで、取得結果が配列に累積されてしまうバグを修正。
v 0.2.10
例外発生時に終了してしまうバグを修正。
リクエスト成否等のステータスの詳細化。
パラメータの文字列による指定からシンボルによる指定への変更。
v 0.2.8 0.2.9
MylistAtomパーサが再生数等を認識しない問題を修正。
Searcherメソッドのブロック内の第2仮引数を、それまでのアクセス成否情報を含むハッシュオブジェクトに変更。
v 0.2.7
ドキュメントのSearcherモジュールの例の誤り、その他細かい点を訂正。
MylistのAtomフィードから、投稿日の取得が漏れていた点を修正。
v 0.2.6
- ドキュメントが正しく生成されていなかったので訂正。
v 0.2.5
ヘッダの追加
コードと設定の分離。
v 0.2.4
ドキュメントの作成。
Searcherループのバグ修正。
Searcherループの継続判定を、ブロック内で
"continue"
を返す事を要求する方式に変更。