ActionScript3.0でニコニコ動画からコメントをダウンロードする。
NNDDの機能拡張ということで、ここ1週間ほど暇を見つけては実装してきました。
ニコニコ動画から、特定の動画のコメント(xml形式)をダウンロードします。
以下のサイトを参考にしました。
以下、処理の流れです。
- 動画に対して"見ているフリ"をする
- "http://www.nicovideo.jp/api/getflv?v=<ビデオID>"にGETリクエストを投げる
- GETリクエストに対するレスポンスで以下を得る
- スレッドID
- コメント取得用API(アドレス)
- 取得したコメント取得用APIに対して所定のXMLをPOSTする
- POSTに対するレスポンスとしてコメント(XML)が得られる
"http://www.nicovideo.jp/api/getflv?v=<ビデオID>"にリクエストを投げるのは、Adobe AIRでニコニコ動画にアクセスして、FLVをダウンロードしてみるでも説明した通りです。
流れが変わるのはその後です。
得られたレスポンスは、例えば
thread_id=1213477606&l=292&url=http://smile-pso61.nicovideo.jp/smile?v=3660857.64921&link=http://www.smilevideo.jp/view/3660857/573999&ms=http://msg.nicovideo.jp/41/api/&user_id=573999&is_premium=1&nickname=MineAP&time=1213521953&done=true&ng_rv=1
のようになっています。この中の、
- thread_id=************
- ms=http://msg.nicovideo.jp/**/api/
に着目します。上はスレッドID、下がコメント取得用APIのアドレスです。
上のスレッドIDを使って、以下のようなXMLを作ります。
<thread res_from="-500" version="20061206" thread="<スレッドID>" />
- res_form:コメントをいくつ取得するか。今回は最新500件。
- version:FlushPlayerのバージョン?固定でいいらしい。
- thread:スレッドID。
これを、コメント取得用APIにPOSTします。するとレスポンスとして、
<?xml version="1.0" encoding="UTF-8"?><packet><thread last_res="5753" result ...(以下省略)
のようなXML形式のデータを取得することができます。
上のXMLデータの中に、
<chat anonymity="1" date="*************" mail="184" no="4651" thread="**********" user_id="********" vpos="2382">うますぎわろたw</chat>
という形式がたくさん入ってます。これ一つのセットがコメントですね。
このうち、
- vpos="2382"
というのが、コメントのタイミングだと思われます。(多分再生開始から2382ミリ秒という意味)
後はこのコメントの山をダウンロードしたビデオの上に描画してやればいいわけです!
この部分を実装し終わってから次のリリースをしたいと思ってます。
上記の処理の部分のコードを抜き出して貼っておきます。自己責任でご利用ください。
public static const VIDEO_DOWNLOAD:int = 0; public static const COMMENT_DOWNLOAD:int = 1; private var downLoader:URLLoader; private var videoLoader:URLLoader; private var commentLoader:URLLoader; /** * FLVのURLを取得する為のAPIへのアクセスを行う * @param videoID 英数字2文字+数字 で表されるビデオのID * @param type ダウンロード対象がVideoか、Commentか。 * @return APIへのリクエストを行うURLLoader * */ private function getAPIResult(videoID:String, type:int = VIDEO_DOWNLOAD):URLLoader { var loader:URLLoader = new URLLoader(); //FLVのURLを取得する為にニコニコ動画のAPIにアクセスする var getAPIResult:URLRequest; getAPIResult = new URLRequest("http://www.nicovideo.jp/api/getflv?v=" + videoID); getAPIResult.method = "GET"; if(type == VIDEO_DOWNLOAD){ loader.addEventListener(Event.COMPLETE, accessAPISuccessForVideo); }else if(type == COMMENT_DOWNLOAD){ loader.addEventListener(Event.COMPLETE, accessAPISuccessForComment); } loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); loader.addEventListener(ProgressEvent.PROGRESS, progressHandler); loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); loader.load(getAPIResult); //注意!この戻り値をdownLoaderに入れておくこと! return loader; } /** * APIへのアクセスに成功したら呼ばれる。 * @param evt * */ private function accessAPISuccessForComment(evt:Event):void { this.label_status.text = "アドレス取得に成功"; trace("アドレスの取得に成功" + evt); commentLoader = this.getComment(downLoader); } /** * コメントを取得するところ。 * @param getApiResultLoader * @return * */ private function getComment(getApiResultLoader:URLLoader):URLLoader { //<thread res_from="-500" version="20061206" thread="スレッドID" /> //thread_id=1213477606&l=292&url=http://smile-pso61.nicovideo.jp/smile?v=3660857.64921&link=http://www.smilevideo.jp/view/3660857/573999&ms=http://msg.nicovideo.jp/41/api/&user_id=573999&is_premium=1&nickname=MineAP&time=1213521953&done=true&ng_rv=1 trace(unescape(getApiResultLoader.data)); //APIから得られたデータの"thread_ID="にあるスレッドIDを探す var threadId:String = getApiResultLoader.data.substring(getApiResultLoader.data.indexOf("thread_ID=")+11, getApiResultLoader.data.indexOf("&")); //APIから得られたデータの"&ms="にあるURLを探す var commentURL:String = getApiResultLoader.data.substring(getApiResultLoader.data.indexOf("&ms=")+4, getApiResultLoader.data.indexOf("&", getApiResultLoader.data.indexOf("&ms")+1)); var getComment:URLRequest = new URLRequest(unescape(commentURL)); getComment.method = "POST"; getComment.requestHeaders = new Array(new URLRequestHeader("Content-Type", "text/html")); videoType = ".xml"; //var xml:String = "<thread fork=\"1\" user_id=\"" + user_id + "\" res_from=\"1000\" version=\"20061206\" thread=\"" + threadId + "\" />"; var xml:String = "<thread res_from=\"-500\" version=\"20061206\" thread=\"" + threadId + "\" />"; getComment.data = xml; var loader:URLLoader = new URLLoader(); loader.dataFormat=URLLoaderDataFormat.TEXT; loader.addEventListener(Event.COMPLETE, commentLoadSuccess); loader.addEventListener(ProgressEvent.PROGRESS, progressHandler); loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); loader.load(getComment); //http://msg.nicovideo.jp/41/api/,<thread res_from="-500" version="20061206" thread="1213477606" /> trace(getComment.url + "," + unescape(new String(getComment.data))); trace("xml取得開始"); return loader; } /** * xmlのダウンロードが完了したら呼ばれる * @param evt * */ private function commentLoadSuccess(evt:Event):void { this.label_status.text = "XMLダウンロード成功" trace("XMLダウンロード成功" + evt); //trace(commentLoader.data) //<?xml version="1.0" encoding="UTF-8"?><packet><thread last_res="5753" result ...(以下省略) if(this.saveComment(commentLoader)){ this.label_status.text = "XML保存完了"; }else{ this.label_status.text = "XML保存失敗"; } this.qOrE(); }