未来のために、ニコニコ動画にHTTPリクエストでコメントする手順をまとめてみることにしました。
目次
- 概要
- 認証
- スレッドIDとメッセージサーバを取得
- ブロックナンバーとチケットを取得
- ポストキーを取得
- 書き込み
概要
ニコニコ動画のコメント書き込みは「メッセージサーバ」と呼ばれるサーバに対して行います。
「www.nicovideo.jp」ではなく「msg.nicovideo.jp」にHTTPリクエストを送信するわけですね。
リクエストを送信する先のURLは動画によって違うので、まずメッセージサーバのURLを取得してから、そこにコメントを送信する流れになります。
コメントを書き込むまでにいろんなAPIを呼ぶわけですが、「www.nicovideo.jp」と「msg.nicovideo.jp」でデータ形式が異なります。
「www.nicovideo.jp」はGETメソッドでリクエストパラメータを送信し、レスポンスデータは「key1=value1&key2=value2&...」という形式。
「msg.nicovideo.jp」はPOSTメソッドでXMLデータを送信し、レスポンスとデータもXML形式で返ってきます。
この違いを押さえておくと混乱することが少なくて済むかもしれません。
認証
まずはユーザ認証をしなくてはいけません。以下のURLにPOSTメソッドでメールアドレスとパスワードを送信します。
https://secure.nicovideo.jp/secure/login?site=niconico
送信するデータはこんな形式です。
mail=[メールアドレス]&password=[パスワード]
ユーザ認証に成功するとレスポンスコード302が返ってきてニコニコ動画のトップページにリダイレクトされます。
その際CookieにセッションIDが書き込まれるのでこれをメモしておきます。
Cookieに書き込まれるセッションIDの値はこんな形式です。
user_session=user_session_******_****************
最初の「******」がログインしたユーザのユーザIDになっているようです。後々必要になってくるのでこれもメモしておきます。
以降のリクエストには全てこのセッションIDをCookieとして送信します。
スレッドIDとメッセージサーバを取得
「getflv」というAPIを使って動画の情報を取得します。
CookieでセッションIDを送りつつ以下のURLにリクエストを送信。「sm9」のところは対象動画の番号で置き換えてください。
http://www.nicovideo.jp/api/getflv/sm9
こんなデータが返ってきます。
thread_id=1173108780&l=320&url=http%3A%2F%2Fsmile-com00.nicovideo.jp%2Fsmile%3Fv%3D9.0468&link=http%3A%2F%2Fwww.smilevideo.jp%2Fview%2F9%2F123456&ms=http%3A%2F%2Fmsg.nicovideo.jp%2F10%2Fapi%2F&user_id=123456&is_premium=0&nickname=%E3%83%8B%E3%83%83%E3%82%AF%E3%83%8D%E3%83%BC%E3%83%A0&time=1244475413&done=true&ng_rv=1&hms=hiroba-test4.nicovideo.jp&hmsp=2528&hmst=1000000064&hmstk=1244475443.Vqtzb_e9yV5_Z72ZyH_XJ1284Ps
見やすくするとこう
thread_id=1173108780
l=320
url=http://smile-com00.nicovideo.jp/smile?v=9.0468
link=http://www.smilevideo.jp/view/9/123456
ms=http://msg.nicovideo.jp/10/api/
user_id=123456
is_premium=0
nickname=ニックネーム
time=1244475413
done=true
ng_rv=1
hms=hiroba-test4.nicovideo.jp
hmsp=2528
hmst=1000000064
hmstk=1244475443.Vqtzb_e9yV5_Z72ZyH_XJ1284Ps
取得したデータから「ms」と「thread_id」の値をメモしておきます。
thread_id=1173108780
ms=http://msg.nicovideo.jp/10/api/
「thread_id」動画についたコメントの一覧を「スレッド」と呼ぶみたいです。2ちゃんねるの「スレ」と同じような感じですね。そのスレッドを識別するIDが「thread_id」です。コメントを書き込むときにはこの「スレッド」に対して書き込むわけですね。
「ms」はメッセージサーバのURLです。コメントを書き込むときにリクエストを送信する対象になります。
ブロックナンバーとチケットを取得
次にメッセージサーバにリクエストをPOSTで送信してスレッドの情報を得ます。
リクエストの送信先はさっき取得した「ms」の値です。
POSTするデータは以下のXMLです。認証で取得したユーザIDと、getflvで取得したスレッドIDを指定します。
CookieでセッションIDを送信するのも忘れずに。
<packet>
<thread thread="[スレッドID]"
version="20061206"
res_from="-1"
user_id="[ユーザID]"/>
</packet>
上のデータは見やすいように改行してありますが、実際にこのまんまリクエストを送信したら失敗しました。改行しないで空白もあけず、以下のようにビッチリと書いたらうまくいきました。
<packet><thread thread="[スレッドID]" version="20061206" res_from="-1" user_id="[ユーザID]"/></packet>
レスポンスでこんなデータが返ってきます。(実際は一行です)
<?xml version="1.0" encoding="UTF-8"?>
<packet>
<thread click_revision="17708" last_res="3442189" num_clicks="4" resultcode="0" revision="1" server_time="1244477788" thread="1173108780" ticket="0x8c37668"/>
<view_counter id="sm9" mylist="63949" video="5765320"/>
<chat anonymity="1" date="1244477408" mail="184" no="3442189" thread="1173108780" user_id="-4CwxVGMJZCYf7rl_i5VzCQPh4U" vpos="19422">うううううううううううううううううううううううううううううううううううううううううううううううううう</chat>
<num_click count="37" no="3426371" thread="1173108780"/>
<num_click count="97" no="3426399" thread="1173108780"/>
<num_click count="10" no="3426457" thread="1173108780"/>
<num_click count="45" no="3426468" thread="1173108780"/>
</packet>
レスポンスデータのXMLから<thread>要素の「last_res」と「ticket」属性をメモしておきます。
「last_res」は最後のコメント番号、というかコメントの数ですね。これを100で割って小数点以下を切り捨てた値がブロックナンバーになるようです。このブロックナンバーが何だかよく分からずに困ってたところをコメント欄で教えてもらいました。感激です。
「ticket」はよくわかりませんけどコメントするときに必要なのでメモしておきます。
ポストキーを取得
ここまでがんばってもまだコメントを書き込むことができません。くじけそうです。あと一息なのでがんばりましょう。
getpostkeyというAPIを呼んでポストキーを取得します。
CookieでセッションIDを送りつつ以下のURLにリクエストを送信。getflvで取得したスレッドIDと、さっきlast_resを100で割って求めたブロックナンバーを指定します。
http://www.nicovideo.jp/api/getpostkey/?yugi=&block_no=[ブロックナンバー]&thread=[スレッドID]
値のない「yugi」というパラメータはなんなんでしょう。リバースなカードをオープンする人でしょうか。よくわかりません。
こんな形式でポストキーが返ってきます。メモしておきましょう。
postkey=***************************
書き込み
長い道のりでしたが、以上でやっとコメントを書き込む準備ができました。
getflvで取得したメッセージサーバのURLに向けて以下のデータをPOSTします。
何度も繰り返しでしつこいですが、CookieでセッションIDも送ります。
<chat thread="[スレッドID]"
vpos="[動画内の時間(1/100秒)]"
mail="184 "
ticket="[チケット]"
user_id="[ユーザID]"
postkey="[ポストキー]">
[コメント]
</chat>
これもやっぱり改行空白をあけずにビッチリと書いた方がいいような気がします。
<chat thread="[スレッドID]" vpos="[時間]" mail="184 " ticket="[チケット]" user_id="[ユーザID]" postkey="[ポストキー]">[コメント]</chat>
指定する値の説明
| thread | getflvで取得したスレッドID |
| vpos | コメントを書き込む動画内の時間を指定します。 単位は1/100秒です。 2分34秒56の位置に書き込むなら 2*60*100 + 34*100 + 56 = 15456 といった具合です。 |
| ticket | スレッド情報で取得したチケットの値 |
| user_id | 認証で取得したユーザID |
| postkey | getpostkeyで取得したポストキー |
書き込みが成功するとこんなデータが返ってきます。
<?xml version="1.0" encoding="UTF-8"?>
<packet>
<chat_result no="3442191" status="0" thread="1173108780"/>
</packet>
失敗するとこうなります。
<?xml version="1.0" encoding="UTF-8"?>
<packet>
<chat_result status="1" thread="1173108780"/>
</packet>
サンプルコード
せっかくなのでサンプルコードを書いてみました。
# 使い方
#
# ruby -Ku nico_comment.rb [mail] [password] [vid] [comment] [vpos]
# mail : メールアドレス
# password: パスワード
# vid : 動画ID(ex.sm9)
# comment : コメント
# vpos : コメント時間(1/100秒)
#
#
# 注意
#
# スクリプトと同じフォルダにhttps://secure.nicovideo.jp/の証明書を
# ファイル名「nicovideo.pem」で保存しておく必要があります
#
# 日本語のコメントはutf-8で書かないといけないみたいなのでDOSプロンプトから実行
# するのは難しそうです...
require 'cgi'
require 'net/http'
require 'net/https'
require "rexml/document"
class NicoClient
attr_accessor :session, :flv, :thread, :postkey
def initialize
@https = Net::HTTP.new('secure.nicovideo.jp',443)
@https.use_ssl = true
@https.ca_file = './nicovideo.pem'
@https.verify_mode = OpenSSL::SSL::VERIFY_PEER
@https.verify_depth = 5
Net::HTTP.version_1_2
end
def login(mail, password)
@https.start do |w|
response = w.post('/secure/login?site=niconico',
"mail=#{mail}&password=#{password}")
return nil if(response.code.to_i != 302)
pattern = /user_session=(user_session_\d+_\d+)/
session_cookie = response.get_fields('set-cookie').find do |cookie|
pattern.match(cookie)
end
@session = pattern.match(session_cookie)[1]
end
end
def getflv(vid)
Net::HTTP.start('www.nicovideo.jp', 80) do |http|
response = http.get("/api/getflv/#{vid}",
"Cookie" => "user_session=#{@session}")
@flv = {}
response.body.split(/&/).each do |elem|
key, val = elem.split(/=/)
@flv[key] = CGI.unescape(val)
end
end
end
def getthread
Net::HTTP.start('msg.nicovideo.jp', 80) do |http|
path = @flv['ms'].gsub(/^http\:\/\/msg\.nicovideo\.jp/, '')
response = http.post(path, self.getthread_data,
"Cookie" => "user_session=#{@session}")
doc = REXML::Document.new response.body
@thread = {}
doc.elements['/packet/thread'].attributes.each do |k,v|
@thread[k] = v
end
end
end
def getthread_data
"<packet>" +
"<thread thread=\"#{@flv['thread_id']}\" " +
"version=\"20061206\" " +
"res_from=\"-1\" " +
"user_id=\"#{@flv['user_id']}\"/>" +
"</packet>"
end
def getpostkey
block_no = @thread['last_res'].to_i / 100
thread_id = @flv['thread_id']
path = "/api/getpostkey/?yugi=&block_no=#{block_no}&thread=#{thread_id}"
Net::HTTP.start('www.nicovideo.jp', 80) do |http|
response = http.get(path,
"Cookie" => "user_session=#{@session}")
response.body.split(/&/).each do |elem|
key, val = elem.split(/=/)
@postkey = CGI.unescape(val)
end
end
end
def comment(message, vpos)
Net::HTTP.start('msg.nicovideo.jp', 80) do |http|
path = @flv['ms'].gsub(/^http\:\/\/msg\.nicovideo\.jp/, '')
response = http.post(path, self.comment_data(message, vpos),
"Cookie" => "user_session=#{@session}")
doc = REXML::Document.new response.body
result = {}
doc.elements['/packet/chat_result'].attributes.each do |k,v|
result[k] = v
end
if(result['status'] == '1') then
nil
else
result['no']
end
end
end
def comment_data(message, vpos)
"<chat thread=\"#{@flv['thread_id']}\" " +
"vpos=\"#{vpos}\" " +
"mail=\"184 \" " +
"ticket=\"#{@thread['ticket']}\" " +
"user_id=\"#{@flv['user_id']}\" " +
"postkey=\"#{@postkey}\">" +
message +
"</chat>"
end
end
email = ARGV[0]
password = ARGV[1]
vid = ARGV[2]
message = ARGV[3]
vpos = ARGV[4]
client = NicoClient.new
if(client.login(email, password) == nil) then
puts 'ログイン失敗'
exit
end
client.getflv(vid)
client.getthread
client.getpostkey
result = client.comment(message, vpos)
if(result == nil) then
puts 'コメント書き込み失敗'
else
puts "コメント成功 resno=#{result}"
end
Trackback URL for this post:
http://blog.smartnetwork.co.jp/staff/trackback/22
from 雪羽の発火後忘失 on 日, 2009/09/06 - 07:03
iPhone/iPod touchアプリ “ニコニコ動画” をパケット解析する(1)より。 結果 キャプチャしたパケットを操作順に見ていく。 起動~ランキング表示 まず、アプリケーションを起動した際に以...
プロペシア 販売