FLYING

〈全日本・紀文豆乳飲料シリーズ「麦芽コーヒー」の500ミリリットルパックを扱う小売店が少ないことに遺憾の意を表明する会〉活動記録

ニコニコ動画のマイリスト動画を一括DL

先日のスクリプトにマイリストのデータを読み込む機能を追加してみた。

うさげ

使い方は nico.rb とほぼ同様です。ただし、引数には動画のIDではなく、マイリストのIDを指定してください。仕様上、短時間に大量のアクセスを行うので、もしかしたらダウンロード中にアクセス規制を食らうかもしれません。ダウンロードはできるだけ人の少ない時間帯に行いましょう。

ruby mylist.rb XXXXXXX

ソース

# mylist.rb

require 'cgi'
require 'uri'
require 'kconv'
require 'net/https'

class Nico
  attr_accessor :video, :video_id, :mylist, :mylist_id, :mylist_array
  
  # ニコニコ動画にログイン
  def initialize(mail, password)
    https = Net::HTTP.new("secure.nicovideo.jp", 443)
    https.use_ssl = true
    https.start {|w|
      data = "site=niconico&next_url=&mail=#{mail}&password=#{password}"
      res = w.post("/secure/login", data)
      @sid = $1 if res["Set-Cookie"] =~ /user_session=user_session([0-9_]+)/
    }
  end
  
  # 動画の情報を取得
  def get_video(video_id)
    @video_id = video_id
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/api/getflv/#{@video_id}", "Cookie" => "user_session=user_session#{@sid}")
      array = CGI.unescape(res.body).split(/&/)
      array.map! {|e| e.split(/=/, 2) }
      @hash = Hash[*array.flatten]
    }
    # ダウンロードに必要なCookieを取得
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/watch/#{@video_id}", "Cookie" => "user_session=user_session#{@sid}")
      @history = $1 if res["Set-Cookie"] =~ /nicohistory=([^;]+)/
      @video = $1 if res.body =~ /<h1.*><a.*>(.*)<\/a><\/h1>/
    }
  end
  
  # マイリストの情報を取得
  def get_mylist(mylist_id)
    @mylist_array = []
    @mylist_id = mylist_id
    res = nil
    Net::HTTP.start("www.nicovideo.jp", 80) {|w|
      res = w.get("/mylist/#{@mylist_id}",
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    res.body.each {|line|
      @mylist = $1 if line =~ /<h1.*>(.*)<\/h1>/
      if line =~ /<h3.*><a.* href="watch\/(.*)">(.*)<\/a><\/h3>/ then
        @mylist_array.push($1)
      end
    }
  end
  
  # 動画をダウンロード
  def get_flv
    uri = URI.parse(@hash["url"])
    res = nil
    Net::HTTP.start(uri.host, uri.port) {|w|
      res = w.get("#{uri.path}?#{uri.query}",
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    return res.body
  end
  
  # コメントをダウンロード
  def get_xml(comments)
    uri = URI.parse(@hash["ms"])
    res = nil
    Net::HTTP.start(uri.host, uri.port) {|w|
      data = "<thread user_id=\"#{@hash["user_id"]}\" res_from=\"#{comments}\""
      data += " version=\"20061206\" thread=\"#{@hash["thread_id"]}\" />"
      res = w.post(uri.path, data,
        "Cookie" => "user_session=user_session#{@sid}; nicohistory=#{@history}")
    }
    return res.body
  end
end

# 準備
nico = Nico.new("YOUR_MAIL", "YOUR_PASSWORD")
nico.get_mylist(ARGV[0])
print "mylist: #{nico.mylist.tosjis}\n"
# 各動画を処理
nico.mylist_array.each {|video_id|
  nico.get_video(video_id)
  title = nico.video.tosjis.gsub(/[\\:*?"<>|\/]/, "_")
  print "saving #{title} ... "
  open("#{title}.flv", "wb") {|f| f.print nico.get_flv }
  open("#{title}.xml", "w") {|f| f.print nico.get_xml(-500) }
  print "done.\n"
}

追記 2008-04-06

動画個別ページの仕様変更に対応。