rubyでニコニコ動画のコメントを取得する

ニコニコ動画ってapiあったの!?
あれ、簡単にコメントだけ取得してくれないの?
くそ、こうなったら作るか、、、
極力gemに頼らないぜ!

というわけで、なかなかに不親切なニコニコ動画のapiと戦っていた。

どうやら、ニコニコ動画のコメントをapi経由で手に入れるには3段階必要で、
1.ニコニコ動画にログイン
2.getflv apiにgetしてスレッドIDなど取得
3.msg.nicovideo.jpにpostしてコメントxml取得
ということらしいです。

まず、ニコニコ動画にログイン、ということで、
https://secure.nicovideo.jp/secure/login?site=niconico
に、自分のメールアドレスとパスワードをpostします。
httpsです。セキュアです。

すると、なんかuser_sessionとかいうクッキーがやたらと帰ってくるので、
その中から、deletedになっていないクッキーを抽出します。

というわけで、メールアドレスとパスワードを渡すと、必要なクッキーを返してくれるものを作った。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ログインしてクッキー抽出
def login_nicovideo(mail, pass)
  host = 'secure.nicovideo.jp'
  path = '/secure/login?site=niconico'
  body = "mail=#{mail}&password=#{pass}"

  https = Net::HTTP.new(host, 443)
  https.use_ssl = true
  https.verify_mode = OpenSSL::SSL::VERIFY_NONE
  response = https.start { |https|
    https.post(path, body)
  }

  cookie = ''
  response['set-cookie'].split('; ').each do |st|
    if idx=st.index('user_session_')
      cookie = "user_session=#{st[idx..-1]}"
      break
    end
  end

  return cookie
end

次に、ニコニコ動画のapiであるgetflvを使います。
下のURLに、さっき選別したクッキーを一緒にgetします。
http://flapi.nicovideo.jp/api/getflv/#{動画ID sm**とか}

すると、以下のような動画情報が帰ってくるので、
お好みで整形します。
今回はハッシュにぶち込み。

でも結局、使うのはthread_idとmsだけなんだよね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# レスポンス
{:thread_id=>"1173108780",
 :l=>"320",
 :url=>"http%3A%2F%2Fsmile-pcm00.nicovideo.jp%2Fsmile%3Fv%3D9.0468",
 :link=>"http%3A%2F%2Fwww.smilevideo.jp%2Fview%2F9%2F1175906",
 :ms=>"http%3A%2F%2Fmsg.nicovideo.jp%2F10%2Fapi%2F",
 :user_id=>"1175906",
 :is_premium=>"1",
 :nickname=>"%E7%81%B0%E5%AD%90",
 :time=>"1311177690",
 :done=>"true",
 :hms=>"hiroba04.nicovideo.jp",
 :hmsp=>"2526",
 :hmst=>"1000000062",
 :hmstk=>"1311177750.c-1OX5rNY8K94qGGdSstdX6BZV0",
 :rpu=>  "%7B%22count%22%3A1330527%2C%22users%22%3A%5B%22%5Cu30b7%5Cu30a2%22%2C%22%5Cu6771%5Cu96f2%22%2C%22%5Cu307a%5Cu30fc%5Cu3061%5Cu3083%5Cu3093%22%2C%22murayuu%22%2C%22%5Cu30ad%5Cu30e3%5Cu30a4%5Cuff5e%5Cu30f3%22%2C%22%5Cu30a2%5Cu30eb%22%2C%22%5Cu3072%5Cu3055%22%2C%22%5Cu305f%5Cu308d%5Cu308d%22%2C%22orycteropus%22%2C%22%5Cu3086%5Cu3063%5Cu3053%22%2C%22%5Cu3042%5Cu3064%5Cu3054%5Cu3093%22%2C%22%5Cu9577%5Cu8c37%5Cu5ddd%5Cu30fb%5Cu30b8%5Cu30e3%5Cu30b3%5Cu30b3%22%2C%22%5Cu307e%5Cu30fc%5Cu304f%5Cu3093%22%2C%22%5Cu304b%5Cu3055%22%2C%22%5Cu3072%5Cu3055%22%2C%22%5Cu653e%5Cu8ab2%5Cu5f8c%22%2C%22%5Cu5fd8%5Cu308c%5Cu3058%5Cu3083%5Cu3044%5Cu3063%22%2C%22%5Cu308a%5Cu3087%5Cu3046%22%2C%22%5Cu304d%5Cu3061%5Cu304f%5Cu3093%22%2C%22getsu%22%5D%2C%22extra%22%3A20%7D"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# getflvから動画情報取得
def get_flv_info(cookie, video_id)
  host = 'flapi.nicovideo.jp'
  path = "/api/getflv/#{video_id}"

  response = Net::HTTP.new(host).start { |http|
    request = Net::HTTP::Get.new(path)
    request['cookie'] = cookie
    http.request(request)
  }

  flv_info = {}
  response.body.split('&').each do |st|
    stt = st.split('=')
    flv_info[stt[0].to_sym] = stt[1]
  end
  flv_info[:ms] =~ /(http%3A%2F%2Fmsg\.nicovideo\.jp%2F)(.*?)(%2Fapi%2F)/
  flv_info[:msg] = $2

  return flv_info
end

そして最後に、さっきのレスポンスにあったmsへ、xmlをpostします。
msは以下みたいな感じ
http://msg.nicovideo.jp/10/api/

xmlの内容は、

1
<thread thread="" version="" res_from="">

threadは、さっきのレスポンスにあったthread_id
versionは、20061206の固定値らしい。
res_fromは取得コメント数。500件ほしかったら、-500 と入れよう!

結果はxmlで帰ってくるので、
オリジナルでコメント部分だけ抽出しちゃいます。

というわけで、コメントの配列を返すものを作った。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# コメントを取得してコメントの配列を返す
def get_comments(flv_info, res_from)
  host = 'msg.nicovideo.jp'
  path = "/#{flv_info[:msg]}/api/"

  response = Net::HTTP.new(host).start { |http|
    request = Net::HTTP::Post.new(path)
    request.body = '<thread thread="'+flv_info[:thread_id]+'" version="20061206" res_from="-'+res_from.to_s+'">'
    http.request(request)
  }

  comments = []
  xml = response.body.gsub('</chat>', "</chat>\n")
  xml.split("\n").each do |line|
    if line =~ /<chat.*?>(.*?)<\/chat>/
      comments << $1
    end
  end

  return comments
end

ちまちま睡眠時間削って早1週間、
長かったぜ・・・。

では、上記の部品を使った例として、
sm9のコメントを100件取得するrubyプログラムはこちら。

自分のメールアドレスとパスワードで試してね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# やっぱsm9か
require 'net/https'

def login_nicovideo(mail, pass)
  host = 'secure.nicovideo.jp'
  path = '/secure/login?site=niconico'
  body = "mail=#{mail}&password=#{pass}"

  https = Net::HTTP.new(host, 443)
  https.use_ssl = true
  https.verify_mode = OpenSSL::SSL::VERIFY_NONE
  response = https.start { |https|
    https.post(path, body)
  }

  cookie = ''
  response['set-cookie'].split('; ').each do |st|
    if idx=st.index('user_session_')
      cookie = "user_session=#{st[idx..-1]}"
      break
    end
  end

  return cookie
end

def get_flv_info(cookie, video_id)
  host = 'flapi.nicovideo.jp'
  path = "/api/getflv/#{video_id}"

  response = Net::HTTP.new(host).start { |http|
    request = Net::HTTP::Get.new(path)
    request['cookie'] = cookie
    http.request(request)
  }

  flv_info = {}
  response.body.split('&').each do |st|
    stt = st.split('=')
    flv_info[stt[0].to_sym] = stt[1]
  end
  flv_info[:ms] =~ /(http%3A%2F%2Fmsg\.nicovideo\.jp%2F)(.*?)(%2Fapi%2F)/
  flv_info[:msg] = $2

  return flv_info
end

def get_comments(flv_info, res_from)
  host = 'msg.nicovideo.jp'
  path = "/#{flv_info[:msg]}/api/"

  response = Net::HTTP.new(host).start { |http|
    request = Net::HTTP::Post.new(path)
    request.body = '<thread thread="'+flv_info[:thread_id]+'" version="20061206" res_from="-'+res_from.to_s+'">'
    http.request(request)
  }

  comments = []
  xml = response.body.gsub('</chat>', "</chat>\n")
  xml.split("\n").each do |line|
    if line =~ /<chat.*?>(.*?)<\/chat>/
      comments << $1
    end
  end

  return comments
end

cookie = login_nicovideo('自分のメールアドレス', '自分のパスワード')
flv_info = get_flv_info(cookie, 'sm9') # sm9からコメントを
comments = get_comments(flv_info, 100) # 100件取得

# コメント表示してるだけ
comments.each do |com|
  puts com
end

以上。