ニコニコ動画のコメント操作に関する解説
■目的
ニコニコ動画のコメント操作に関する解説をします
以下の動画で紹介したツールの実装の説明になる。







■ニコニコ動画でのコメント取得の流れ
基本的な流れは以下の通り
  1. ユーザー情報を指定してログインを要求する。
  2. getflv APIを実行してメッセージサーバのURLとthreadidを取得する
  3. メッセージサーバに対してthreadidを指定してメッセージを要求する

ただし、この手順では最も最近の1000件のみしかコメントは取得できない。

もし、過去のコメントを取得するにはプレミアム会員のユーザーでwaybackkeyを入手してからメッセージサーバーにコメントを要求しなければならない。

  1. ユーザー情報を指定してログインを要求する。
  2. getflv APIを実行してメッセージサーバのURLとthreadidを取得する
  3. getwaybackkey APIを実行してwaybackkeyを取得する。
  4. メッセージサーバに対してthreadidを指定してメッセージを要求する


また、コミュニティー動画の場合はthreadkeyがないとコメントが取得できない

そのため、getthreadkeyをしようしてthreadkeyを取得しなければならない。


  1. ユーザー情報を指定してログインを要求する。
  2. getflv APIを実行してメッセージサーバのURLとthreadidを取得する
  3. getthreadkey APIを実行してthreadkeyを取得する
  4. getwaybackkey APIを実行してwaybackkeyを取得する。
  5. メッセージサーバに対してthreadidを指定してメッセージを要求する




1.ユーザー情報を指定してログインを要求する
接続先:https://secure.nicovideo.jp/secure/login
method:POST
POSTの内容:
mail=メールアドレス&password=パスワード

リクエストヘッダ:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: POSTデータのバイト数
Connection: keep-alive
この応答は下記の通り


ステータスコード:302 Found
リクエストしたリソースが移動されている。
クライアントはLocationヘッダに指定されている下記のURLにリダイレクトする。
https://secure.nicovideo.jp/secure/

レスポンスヘッダ:
Connection: close
Date: Wed, 27 Nov 2013 06:10:33 GMT
Content-Length: 0
Content-Type: text/html
Content-Language: ja
Location: https://secure.nicovideo.jp/secure/
Server: Apache
Set-Cookie: nicosid=数字; expires=Sat, 25-Nov-2023 07:05:14 GMT; path=/; domain=.nicovideo.jp
Set-Cookie: user_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT
Set-Cookie: user_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/
Set-Cookie: user_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=.nicovideo.jp
Set-Cookie: user_session=セッションを区別するID; expires=Fri, 27-Dec-2013 06:10:33 GMT; path=/; domain=.nicovideo.jp
Set-Cookie: repair_history=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=.nicovideo.jp
x-niconico-id: ユーザID
x-niconico-authflag: 3

クライアントはSet-Cookieで指定されたnicosidとuser_sessionを保持しなければならない。
以降の通信において、リクエストヘッダでこれらを設定する必要がある。

user_sessionが存在しない場合は、ログインに失敗したとみなす。


ステータスコードでリダイレクトすることに気を付けなければならない。

MSXMLではライブラリはリダイレクトを明示的に禁止できず、プログラマはリダイレクトされた結果しかうけとることができない。このことは初回の応答で返すSet-Cookieの値を保持できないことを意味する。

VBAで行う場合は、MSXMLではなく、Microsoft WinHTTP Services, version 5.1を使用してリダイレクトを禁止する必要がある。
    Dim http As WinHttpRequest
    Set http = New WinHttpRequest
   
    http.Option(WinHttpRequestOption_EnableRedirects) = False


2.getflv APIを実行してメッセージサーバのURLとthreadidを取得する
接続先:http://flapi.nicovideo.jp/api/getflv/動画ID
method:GET
リクエストヘッダ:
Content-Type:  application/x-www-form-urlencoded
Connection: keep-alive
Cookie:nicosid=1で取得したID; user_session=1で取得したID
リクエストの結果
thread_id=1354189316&l=1815&url=http%3A%2F%2Fsmile-psu12.nicovideo.jp%2Fsmile%3Fm%3D19466130.25345&link=http%3A%2F%2Fwww.smilevideo.jp%2Fview%2F19466130%2F854409&ms=http%3A%2F%2Fmsg.nicovideo.jp%2F31%2Fapi%2F&ms_sub=http%3A%2F%2Fsub.msg.nicovideo.jp%2F31%2Fapi%2F&user_id=ユーザID&is_premium=1&nickname=ユーザー名&time=1385536738608&done=true&hms=hiroba.nicovideo.jp&hmsp=2544&hmst=200&hmstk=1385536798.sPGlPCR8FQjgXw07-OyjgcROggw
このリクエストの結果で必要なのは下記の項目である。
thread_id : スレッドID
ms: メッセージサーバのURL
user_id:ユーザーID
is_premium:1ならばプレミアム会員

このリクエストかえってくるURLの記号は16進数表記されているので、これを元に戻す必要がある。
http%3A%2F%2Fmsg.nicovideo.jp%2F31%2Fapi%2F
⇒http://msg.nicovideo.jp/31/api/

この処理に関しては「VBAで文字のescapeとunescapeを行なう」を参考のこと。


3. getthreadkey APIを実行してthreadkeyを取得する
接続先:http://flapi.nicovideo.jp/api/getthreadkey?thread=スレッドID

method:GET
リクエストヘッダ:
Content-Type:  application/x-www-form-urlencoded
Connection: keep-alive
Cookie:nicosid=1で取得したID; user_session=1で取得したID

リクエストの結果
threadkey=スレッドキー&force_184=1

このリクエストの結果でthreadkeyとforece_184が取得できる。
コメントを取得する際に、ここで取得できた値を指定する。

なお、コミュニティー動画でないふつうの動画の場合は、このスレッドキーはブランクである。

4.getwaybackkey APIを実行してwaybackkeyを取得する。
プレミアム会員のユーザーに限りこの処理を行う。

接続先:http://flapi.nicovideo.jp/api/getwaybackkey?thread=スレッドID

method:GET

リクエストヘッダ:
Content-Type:  application/x-www-form-urlencoded
Connection: keep-alive
Cookie:nicosid=1で取得したID; user_session=1で取得したID

リクエストの結果
waybackkey=WayBackKey

このリクエストの結果でWayBackKeyが取得できる

5.メッセージサーバに対してthreadidを指定してメッセージを要求する

method:GET
リクエストヘッダ:
Content-Type:  text/xml
Connection: keep-alive
Cookie:nicosid=1で取得したID; user_session=1で取得したID

通常会員で過去ログの取得が不可能な場合のPOSTの内容:
<thread thread="スレッドID" version="20090904" res_from="-1000" scores="1" nicoru="1" threadkey="gethtreadkeyで取得した値" force_184="gethtreadkeyで取得した値" user_id="ユーザID" />

プレミアム会員で過去ログの取得ができる場合のPOSTの内容:
<thread thread="スレッドID" version="20090904" res_from="-1000" scores="1" nicoru="1" waybackkey="getwaybackkeyで取得したID" threadkey="gethtreadkeyで取得した値" when="1354710869" user_id="ユーザID" force_184="gethtreadkeyで取得した値" />
res_fromは現在または、whenで指定した日付より何件さかのぼって取得するかを指定する。この上限は-1000と指定した場合の1000件になる。

whenはコメントを取得する基準になる日付である。省略した場合は現在。
この時刻はUNIX時刻になる。
すなわち、標準時刻の1970/1/1からの経過秒数である。
このUNIX時刻とVBAのDATE型は次のようにして相互変換すること。

'! 以下を参考にしました。
'! http://zakizaki.cocolog-nifty.com/software/2009/05/tracxmlrpcvba-9.html

'http://www.cpearson.com/excel/TimeZoneAndDaylightTime.aspx
Private Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' NOTE: If you are using the Windows WinAPI Viewer Add-In to get
' function declarations, not that there is an error in the
' TIME_ZONE_INFORMATION structure. It defines StandardName and
' DaylightName As 32. This is fine if you have an Option Base
' directive to set the lower bound of arrays to 1. However, if
' your Option Base directive is set to 0 or you have no
' Option Base diretive, the code won't work. Instead,
' change the (32) to (0 To 31).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Private Type TIME_ZONE_INFORMATION
    Bias As Long
    StandardName(0 To 31) As Integer
    StandardDate As SYSTEMTIME
    StandardBias As Long
    DaylightName(0 To 31) As Integer
    DaylightDate As SYSTEMTIME
    DaylightBias As Long
End Type


''''''''''''''''''''''''''''''''''''''''''''''
' These give symbolic names to the time zone
' values returned by GetTimeZoneInformation .
''''''''''''''''''''''''''''''''''''''''''''''

Private Enum TIME_ZONE
    TIME_ZONE_ID_INVALID = 0        ' Cannot determine DST
    TIME_ZONE_STANDARD = 1          ' Standard Time, not Daylight
    TIME_ZONE_DAYLIGHT = 2          ' Daylight Time, not Standard
End Enum


Private Declare Function GetTimeZoneInformation Lib "kernel32" _
    (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
'*
'* 標準時刻またはローカル時刻に調整する
'* @param[in] gmt GMT時刻 "Thu, 1-Jan-2030 00:00:00 GMT"
'* @param[in] sign +1 のときはGMT->ローカル日時 -のときはローカル->GMT
'* @return 調整時刻
'*
Public Function AdjustTimeDifference(ByVal gmt As Date, Optional sign As Long = 1)
    Dim tzi As TIME_ZONE_INFORMATION
    Dim dst As TIME_ZONE
    Static tbias As Long
    Static Init As Boolean
    If Not Init Then
        dst = GetTimeZoneInformation(tzi)
       
        If dst = TIME_ZONE_DAYLIGHT Then
            tbias = tzi.Bias + tzi.DaylightBias
        Else
            tbias = tzi.Bias
        End If
        tbias = tbias / 60 * -1
        Init = True
    End If
    AdjustTimeDifference = DateAdd("h", tbias * sign, gmt)
End Function

'*
'* ユニックスタイムをVBやVBAで使用するDate関数やTime関数にする
'* http://www.jp-ia.com/_ans/DateUnixConversion.html
'* @param[in] dblDate UNIXの時刻
'* @return VBAの日付
'*
Function UnixDateConversion(dblDate As Double) As Date
'"yyyy/mm/dd hh:mm:ss"の書式で表される値を返します。
    ' 1. UNIX タイム 1970/01/01 0:0:0 からの通算の秒数
    ' 2. VBA         1899/12/30 00:00:00 からの経過日数、少数点以下が時刻
    ' 3  25569 は 1,2の日付の差
    ' 時差で調整
    UnixDateConversion = AdjustTimeDifference((dblDate / 86400) + 25569)
End Function
'*
'* VBやVBAで使用するDate関数やTime関数をユニックスタイムにする
'* @param[in] dblDate VBAの時間
'* @return UNIX時間
'*
Function DateUnixConversion(dblDate As Date) As Double
    DateUnixConversion = DateDiff("s", "1970/01/01 00:00:00", AdjustTimeDifference(dblDate, -1))
End Function

このPOSTの結果として、XMLが返ってくる。
そこにコメント情報が記述してある。
ただし、使用している文字コードがUTF8のためVBAで取り扱える形で返す必要がある。

下記のページのようにADOのライブラリを使用するのがお手軽である。
http://dukesoftware00.blogspot.jp/2009/11/adodbstreambyteexcel-vba.html

すなわち、WinHttpRequestでリクエストを発行したらResponseBodyのバイト配列を上記の方法でエンコードすればよい。

全部のコメントを取得する場合は、取得したXMLの「date」属性で最も小さいものを取得して、次回実行する場合に、whenにこの値を指定する。





[PR]
by mima_ita | 2013-11-27 17:14 | VBA
<< 配列を経由したセルの値設定の副作用 NMecabを用いてVBAやW... >>



実験ですお
検索
カテゴリ
最新の記事
.NET4.5におけるasy..
at 2014-07-02 00:46
.NETでTwitterを検..
at 2014-06-29 00:49
Redmineのプラグインで..
at 2014-06-28 03:29
IO.popenのwrite..
at 2014-06-28 03:25
RedmineのWikiでU..
at 2014-06-28 03:16
以前の記事
最新のトラックバック
その他のジャンル
ブログパーツ