最近の休日プログラミングの方はひたすらRuby。やっぱりJavaに比べると実装時間が短くて済みます。
昨日たまたまタイトルのようなRubyスクリプトが欲しくなって自作。現在Ubuntuのcronタスクとして実行されるようになっているものを紹介します。
ファイルは、以下のncomment.rbとlist.txtを同じフォルダに作って、あとは"ruby ncomment.rb"と実行するだけです。結果は以下の通り。動作は、Ubuntu8.04(Ruby 1.8.6)とMacOSX10.5.7(Ruby 1.8.7)確認済です。
- ncomment.rb
#!/usr/bin/env ruby -Ku # -*- coding: utf-8 -*- # このスクリプトは、ニコニコ動画のURLのリストから # 指定した時間内に投稿されたコメントを最大500件収集し、 # Gmailから結果のメールを送信してくれるスクリプトです。 # 設定項目1(ニコニコ動画アクセス)と設定項目2(Gmail)があります ####################################################### require 'yaml' require 'rubygems' require 'mechanize' require 'cgi' require "rexml/document" require 'net/smtp' require 'tlsmail' require 'base64' $KCODE = 'u' class Nicovideo LOGIN_URL = 'https://secure.nicovideo.jp/secure/login?site=niconico' WATCH_URL = 'http://www.nicovideo.jp/watch/' FLV_URL = 'http://www.nicovideo.jp/api/getflv?v=' INFO_URL = 'http://www.nicovideo.jp/api/getthumbinfo/' def initialize(url) @video_id = url.scan(/sm\d+$/).first @agent = WWW::Mechanize.new end def login(mail, passwd) @agent.post(LOGIN_URL, {'mail' => mail, 'password' => passwd}) end def setURL(url) @video_id = url.scan(/sm\d+$/).first end def save_comment(filename) @agent.get_file(WATCH_URL + @video_id) content = @agent.get_file(FLV_URL + @video_id) params = content.scan(/([^&]+)=([^&]*)/).inject({}){|h, v| h[v[0]] = v[1]; h} puts "#{@video_id} saving comments as #{filename}\n" comment_host, path = %r{http://([\w\.]+)(.*)}.match(CGI.unescape(params['ms'])).to_a.values_at(1,2) thread_id = params['thread_id'] body = %!<thread res_from="-500" version="20061206" thread="#{thread_id}" />! comments = Net::HTTP.start(comment_host, 80) {|http| response = http.post(path, body) response.body } File.open("#{filename}", "wb") {|f| f.write comments } end def save_thumbinfo(filename) content = @agent.get_file(INFO_URL + @video_id) file = File.open("#{filename}", "wb") file.print content file.close end end #class Nicovideo ######################## class Gmail Port = 587 HeloDomain="gmail.com" SmtpHost="smtp.gmail.com" def self.send(from, passwd, to, subject, message) smtpserver = Net::SMTP.new(SmtpHost, Port) smtpserver.enable_tls(OpenSSL::SSL::VERIFY_NONE) header_body = <<EOD From: #{from} To: #{to} Date: #{Time::now.strftime("%a, %d %b %Y %X %z")} X-Mailer: imput.rb Subject: #{subject} #{message} EOD smtpserver.start('gmail.com', from, passwd, :login) do |smtp| smtp.send_message header_body, from, to end end end #class Gmail ######################## if $0 == __FILE__ Dir::chdir(File::dirname(__FILE__)) ####### 設定項目1 ####### listFilename = "list.txt" # 動画のURLのリスト id = "[email protected]" # ニコニコ動画のidのメールアドレス pass = "hoge" # ニコニコ動画のpassward secWait = 8 # 連続取得をするとエラーが出るので、そのための待ち時間 (秒) hour = 6 # 時間前までのコメントのダイジェストを作成 # コメントファイルを作るフォルダを作成 if !(File.exists?("./comment")) then Dir::mkdir("./comment") end # ファイルを開いてログインした後、ひたすらコメントと動画情報取得 file = open(listFilename) isFirst = true while text = file.gets do next if /^\s*$/ =~ text next if /^#/ =~ text if isFirst then nico = Nicovideo.new(text) nico.login(id , pass) isFirst = false else nico.setURL(text) end begin sleep(secWait) nico.save_comment('./comment/' + text.scan(/sm\d+$/).first + '_c.xml') nico.save_thumbinfo('./comment/' + text.scan(/sm\d+$/).first + '_i.xml') rescue => ex errorCount += 1 if errorCount < 10 then # 9回まで再チャレンジ secRetry = 180 * errorCount print "wait #{secRetry} sec and retry #{errorCount} : #{text} \n" sleep(secRetry) retry else print ex.message, "\n" end end end # commentフォルダのXMLを調べてコメントのダイジェストを取得 body = "" intFrom = (Time.now - hour*60*60).to_i # 取得する時間のUNIX秒 Dir::foreach("./comment"){ |f| if /_c.xml/ =~ f then print "processing : " + "./comment/" + f + "\n" xmlComment = "" file = File.open("./comment/" + f) while text = file.gets do s = text.gsub(/(<\/[a-zA-Z]+>|\/>)/){|matched| matched + "\n" } xmlComment.concat s end file.close doc = REXML::Document.new xmlComment nodes = REXML::XPath.match(doc, "/packet/chat") docInfo = REXML::Document.new File.open("./comment/" + f.gsub(/_c/, "_i") ) title = REXML::XPath.match(docInfo, "/nicovideo_thumb_response/thumb/title")[0].text viewCount = REXML::XPath.match(docInfo, "/nicovideo_thumb_response/thumb/view_counter")[0].text commentCount = REXML::XPath.match(docInfo, "/nicovideo_thumb_response/thumb/comment_num")[0].text mylystCount = REXML::XPath.match(docInfo, "/nicovideo_thumb_response/thumb/mylist_counter")[0].text isExist = false nodes.each{ |node| intDate = node.attributes["date"].to_i if intDate > intFrom then if !isExist then body.concat "\n" + title + "\n" body.concat "再生:" + viewCount + " コメント:" + commentCount + " マイリスト:" + mylystCount + "\n" isExist = true end body.concat "\t" + (node.text == nil ? "" : node.text ) body.concat " (" + Time.at(intDate).strftime("%Y/%m/%d %H:%M:%S") + ")\n" end } end } ####### 設定項目2 ####### print body Gmail.send("[email protected]", "password", "[email protected]", "#{hour}時間以内のニコ動コメント" ,body) # 不要になったcommentフォルダを消す dirlist = Dir::glob("./comment/" + "**/").sort { |a,b| b.split('/').size <=> a.split('/').size } dirlist.each {|d| Dir::foreach(d) {|f| File::delete(d+f) if ! (/\.+$/ =~ f) } Dir::rmdir(d) } print "finished!\n" end
- list.txt
http://www.nicovideo.jp/watch/sm7736286 http://www.nicovideo.jp/watch/sm7689160
- 送られてくるメールの本文(件名: 6時間以内のニコ動コメント)
かゆいところに足が届かぬ猫 再生:119382 コメント:1337 マイリスト:2352 ぴょこぴょこ (2009/07/26 08:53:14) リアルにゃんこ先生wwwww (2009/07/26 10:25:55) しぇーーー!! (2009/07/26 11:40:19) ぜんぜん届いてない件 (2009/07/26 11:51:41) ふといw (2009/07/26 12:55:46) wwwwwwwww (2009/07/26 12:55:55) 声がエロい亀 再生:51672 コメント:2065 マイリスト:1103 声帯丸見えwww (2009/07/26 08:28:59) wwwwwwwwwwww (2009/07/26 08:31:39) えええええええええええええええ (2009/07/26 08:31:42) www (2009/07/26 08:32:08) ええええええええええええええええええええええええええええ (2009/07/26 08:32:09) エロ過ぎるwwwwwwwwwwwww (2009/07/26 08:32:13
設定は1と2があるので、好きなように変えて動かしてください。無論、rubyとrubygemsが必要です。インストールは各プラットホームごとのやりかたをググってもらえればと思います。
あと、"mechanize"と"tlsmail"が別途必要なので、"sudo gem install mechanize" と "sudo gem install tlsmail"のインストールをお忘れなく。何気にUbuntuの方は、mechanizeを入れるのに、apt-getで"ruby1.8-dev"、"hpricot"、"libxml2-dev"、"libxslt1-dev"を入れないと"sudo gem1.8 install mechanize"でインストールできませんでした。
このスクリプトを作るのに以下のサイトから二つのクラスを拝借、改造させてもらいました。
ありがとうございました。
議論が活発な動画や、コメントを書いた後の動向が気になる動画のコメントを自動チェックさせたりするのに役立つかもしれません。なお、ニコニコ動画のコメント取得APIは連続でアクセスしまくると繋がらなくなります。一応待ち時間を設定できるようにしてありますが、30件ぐらいならば待ち時間8秒ぐらいでなんとかなりました。数百件となになると、10秒以上待った方がよいかもしれません。
追伸
リトライ機構を設けておきました。連続取得失敗した際も時間はかかりますが復旧してくれるはずです。待ち時間15秒で、400件の動画でも2時間ほどかかりますがやり終えてくれるようです。