理系学生日記

おまえはいつまで学生気分なのか

Youtube から flv をダウンロードするスクリプト

Ruby でもうちょっと練習をしたかったので,指定した文字列で Youtube に検索をかけ,指定文字列に関連する flv ファイルをダウンロードするスクリプトなどを書いてみた.mpeg4 じゃなくて flv かよみたいな時点でどうなのそれみたいな話もありそうだけど無視.
実行はこんな感じ.

$ ./youtube.rb 猫鍋
猫鍋っぽい動画.flv...
猫鍋にぬるま湯を張ってみました.flv...
BOSS 猫鍋入浴.flv...

Ruby 系の本を読んだ絶対量が半端なく少ないし,Ruby のプログラムを読んだ絶対量も半端なく少ないので,どういう書き方のマナーがある言語なのかがイマイチまだわかってない.

#!/usr/bin/ruby
require 'rubygems'
require 'mechanize'
require 'scrapi'

$KCODE = 'u'

class YoutubeDownloader 
  attr_accessor :title

  YOUTUBE_URL = 'http://jp.youtube.com'

  # コンストラクタ
  def initialize( title )
    @title = title
    @agent = WWW::Mechanize.new
  end

  # 検索結果のうち,何個のファイルを保存するか
  def save_n( num = 1 ) 
    info = submit_query
    info[ 0 ... num ].each { |i| 
      i.save 
    }
  end

  private

  # 最初の検索結果のページから,検索結果として出力されたファイルの情報を抜き出す
  def submit_query()
    # query を submit 
    page = @agent.get( YOUTUBE_URL )
    search_form = page.form( 'searchForm' )
    search_form['search_query'] = @title
    search_form['search_sort'] = 'video_view_count'
    page = @agent.submit( search_form )

    # 返ってきたページからファイルのタイトルとパスを抜き出す
    scraper = Scraper.define do
      process 'div[class="vlentry"]>div[class="vlcontainer"]>div[class="vldescbox"]>div[class="vltitle"]>div[class="vlshortTitle"]>a', "urls[]" => "@href", "linktext[]" => :text
      result :urls, :linktext
    end
    result = scraper.scrape( page.body, :parser_options => { :char_encoding => 'utf8' } )

    files = Array.new
    result.urls.each_index { |idx|
      # あまりに検索にへんなものが引っ掛かるので,タイトルに @title が含まれないものは除去する
      next unless result.linktext[ idx ] =~ /#{@title}/
      files << YoutubeFile.new( result.linktext[ idx ], 
                                YOUTUBE_URL,
                                result.urls[ idx ] )
    }
    files
  end
end

class YoutubeFile
  attr_accessor :title, :baseurl, :path, :hashkey

  def initialize( title, baseurl, path )
    @agent = WWW::Mechanize.new
    @title, @baseurl, @path = title, baseurl, path
  end

  def save
    filename = @title + '.flv'
    # '/' があるとセーブ時にエラーになるので
    filename.gsub( '/', '-' )
    puts filename + "..."
    return if File.exist?( filename );
    video = @agent.get( get_download_path )
    video.save_as( filename )
  end

  private

  def get_download_path
    response = @agent.get( @baseurl + @path )
    response.body =~ /"video_id":\s+"(.+?)".*?"t":\s+"(.+?)"/
    @id, @hashkey = $1, $2
    "/get_video?video_id=#{@id}&t=#{@hashkey}"
  end
end

yd = YoutubeDownloader.new( *ARGV )
yd.save_n( 3 )