バリケンのRuby日記 RSSフィード

2007-02-28

[] Rubyist Magazine 0018 号が出た!  Rubyist Magazine 0018 号が出た! - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyist Magazine 0018 号が出た! - バリケンのRuby日記  Rubyist Magazine 0018 号が出た! - バリケンのRuby日記 のブックマークコメント

Rubyist Magazine - Rubyist Magazine 0018 号

読むよ~。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070228

2007-02-26

[][] 青空文庫図書カードから、その作品のHTML版のURLを得る  青空文庫の図書カードから、その作品のHTML版のURLを得る - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  青空文庫の図書カードから、その作品のHTML版のURLを得る - バリケンのRuby日記  青空文庫の図書カードから、その作品のHTML版のURLを得る - バリケンのRuby日記 のブックマークコメント

ずいぶんサボっちゃったけど、前回の続きだよ。

今回は、前回リストアップした「図書カード」のURLから、その作品のHTML版のURLを調べてみるよ。そろそろちゃんとクラス設計したいところだけど、とりあえずそのままで。

ちなみに今までもそうだけど、Hpricotの使い方はこちらを参考にしているよ。

$KCODE = 's'

require 'open-uri'
require 'nkf'
require 'hpricot'
require 'pp'

book_card = Hpricot.parse(
  NKF.nkf('-s',
    open("http://www.aozora.gr.jp/cards/000020/card2569.html").read
  )
)

pp book_card.at("a[text()*='HTML版で読む']")['href']

実行結果だよ。

"./files/2569.html"

ようやく作品ファイルにたどりついたね。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070226

2007-02-23

[] 日記中のソースコードライセンスについて  日記中のソースコードのライセンスについて - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  日記中のソースコードのライセンスについて - バリケンのRuby日記  日記中のソースコードのライセンスについて - バリケンのRuby日記 のブックマークコメント

以前書いた「Gmailでメールを送る」に、id:garyoさんからトラックバックをいただきました

いままで日記中のソースコードライセンスについて明示していなかったけど、「Rubyライセンス準拠」で自由に使っていいですよ~。

ただし、アイデアを他の人からもらったコードとかもあるから、利用するとき(特に商用)は念のため連絡してくれると嬉しいなあ。たとえば以前書いた「ルート探索(1)」は、コードを書いたのはぼくだけど、アルゴリズムは僕のRuby師匠(誰?)が考えたものだよ。

つまり、ぼくが書いた「著作物としての日記に書かれたコード」の著作権については、通常の著作物と同様に(常識の範囲内で)自由に引用したり言及したり利用したりしてもかまわないけど、実はそれが他の人(第三者)も何らかの権利(たとえばアイデア特許として取得していた、とか)を持っているかも知れないから、そのときはその人(第三者)の権利のことも考えよう、ってことだね。

うーん。それにしても、ライセンスって面倒だねえ。

garyogaryo2007/02/25 16:17ありがとうございます。RubyでGmailを送信したくて色々探して試してみてました。mechanizeってすごいですね。GREEに書けるならMixiにも書き込めるのかな。

muscovyduckmuscovyduck2007/02/25 16:20できますよー>garyoさん
下記がとても参考になります!
http://mono.kmc.gr.jp/~yhara/d/?date=20070205
http://mono.kmc.gr.jp/~yhara/w/?Ruby-WWW-Mechanize

garyogaryo2007/02/25 17:24ありがとうございます。URL先を見てるとgoogle自動検索の例があったので、plugin化できないか挑戦してみます。自動検索できたら何かできないかな。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070223

2007-02-21

[][] 進化する“Webスクレイピング技術の世界  進化する“Webスクレイピング”技術の世界 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  進化する“Webスクレイピング”技術の世界 - バリケンのRuby日記  進化する“Webスクレイピング”技術の世界 - バリケンのRuby日記 のブックマークコメント

進化する“Webスクレイピング”技術の世界 - @IT

へー。「scRUBYt!」って知らなかったなあ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070221

2007-02-20

[] ネットワーク機器の状態を調べる  ネットワーク機器の状態を調べる - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  ネットワーク機器の状態を調べる - バリケンのRuby日記  ネットワーク機器の状態を調べる - バリケンのRuby日記 のブックマークコメント

最近ネットワーク機器は、Webブラウザを使って設定したり状態を参照したりできるよね。

ということは、mechanizeを使ってネットワーク機器の状態を調べたり、設定したりもできそうだよね。

じゃあ、ぼくの利用しているPA-WDR85FH/CECというADSLルータ現在の状態を調べるスクリプトを書いてみよう!ルータへのアクセスにはBASIC認証が必要だから、WWW::Mechanize#basic_authメソッドを使っているのがポイントだよ。

$KCODE = 's'

USERNAME = "(ユーザ名)"
PASSWORD = "(パスワード)"
HOSTNAME = "(ADSLルータのIPアドレス)"

require 'mechanize'
require 'pp'

agent = WWW::Mechanize.new
agent.basic_auth(USERNAME, PASSWORD)

aterm_info_page = agent.get("http://#{HOSTNAME}/info_main.html")
aterm_info = aterm_info_page.search("table:nth-child(1) tr").map {|e|
  e.search("td").map {|f|
    f.inner_text.gsub(/(?:\s| )+/, '')
  }
}
pp aterm_info

実行結果だよ(固有の情報はxxに変更しているよ)。

[["【装置情報】"],
 ["装置名", "WARPSTAR-BaseStation-A9161E"],
 ["ファームウェアバージョン(ベース)", "x.xx"],
 ["ファームウェアバージョン(無線)", "x.xx"],
 ["MACアドレス(WAN/LAN)", "xx:xx:xx:xx:xx:xx"],
 ["MACアドレス(USB)", "xx:xx:xx:xx:xx:xx"],
 ["MACアドレス(無線)", "xx:xx:xx:xx:xx:xx"],
 [""],
 ["【状態表示】"],
 ["動作モード", "PPPoE"],
 ["接続先名", "xxxxxxxx"],
 ["接続状態", "回線接続中"],
 ["PPPoEブリッジ接続状態", "未接続"],
 ["WAN側IPアドレス", "xxx.xxx.xxx.xxx"],
 ["WAN側ネットマスク", ""],
 ["WAN側ゲートウェイ", ""],
 ["WAN側プライマリDNS", "xxx.xxx.xxx.xxx"],
 ["WAN側セカンダリDNS", "xxx.xxx.xxx.xxx"],
 ["WAN側ドメイン名", ""],
 ["LAN側IPアドレス", "xxx.xxx.xxx.xxx"]]

たとえばこの配列からWAN側IPアドレスだけ知りたいときは、次のようにすればいいよね。

pp aterm_info.select {|i| i[0] == "WAN側IPアドレス" }[0][1]

他のネットワーク機器でも似たようなことができるはずだから、チャレンジしてみてね。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070220

2007-02-19

[] GREEダイアリーライターリファクタリング(2)  GREEダイアリーライターのリファクタリング(2) - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  GREEダイアリーライターのリファクタリング(2) - バリケンのRuby日記  GREEダイアリーライターのリファクタリング(2) - バリケンのRuby日記 のブックマークコメント

このあいだのGREEダイアリーライターのリファクタリングの続きだよ。

まずはgree_writer.iniだよ。これは前回と変わっていないよ。

{
  :user_name => '(ユーザ名)', # GREEのユーザー名(登録メールアドレス)
  :password => '(パスワード)', # GREEのパスワード
  :gree_writer_dir => '.', # アップロード済みファイルや元画像ファイルを保管するフォルダ
  :upfile_dir => '.', # アップロードするテキストや画像が置かれたフォルダ
  :photo_resize => false, # アップロードする画像を640ピクセルに変換するかどうか
}

次に、gree_writer.rbだよ。見通しをよくするため、Diaryクラスを定義してみたよ。

$KCODE = 's'

require 'mechanize'
require 'kconv'
require 'fileutils'

module GreeWriter
  class Config
    def initialize (config = {})
      @user_name = config[:user_name]
      @password = config[:password]
      @gree_writer_dir = File.expand_path(config[:gree_writer_dir])
      @upfile_dir = File.expand_path(config[:upfile_dir])
      @photo_resize = config[:photo_resize]
    end
    attr_reader :user_name, :password, :gree_writer_dir, :upfile_dir, :photo_resize

    def self.load (file)
      self.new(eval(File.open(file).read))
    end
  end

  class Diary
    def initialize (text_file, photo_file = [])
      open(text_file) {|f| @body = f.read.to_a }
      @title = @body.shift.chomp.toeuc
      @body = @body.join.toeuc
      @text_file = File.expand_path(text_file)
      @photo_file = photo_file.map {|i| File.expand_path(i) }
    end
    attr_reader :title, :body, :photo_file

    def move_diary (dir, str)
      new_text_file = File.join(dir, 'uploaded', str + ".txt")
      FileUtils.mv(@text_file, new_text_file)
      @text_file = new_text_file

      @photo_file.each_with_index {|item, index|
        new_photo_file = File.join(dir, 'uploaded', str + "_#{index}.jpg")
        FileUtils.mv(item, new_photo_file)
        @photo_file[index] = new_photo_file
      }
    end
  end

  class DiaryWriter
    def initialize (cfg)
      @user_name = cfg.user_name
      @password = cfg.password
      @agent = WWW::Mechanize.new

      make_save_directory(cfg.gree_writer_dir)
    end

    private
    def make_save_directory (dir)
      orig_images_dir = File.join(dir, 'orig_images')
      uploaded_dir = File.join(dir, 'uploaded')

      raise if File.exist?(orig_images_dir) and not File.directory?(orig_images_dir)
      raise if File.exist?(uploaded_dir) and not File.directory?(uploaded_dir)

      FileUtils.mkdir(orig_images_dir) unless File.exist?(orig_images_dir)
      FileUtils.mkdir(uploaded_dir) unless File.exist?(uploaded_dir)
    end

    public
    def login
      login_page = @agent.get("http://gree.jp/")
      login_form = login_page.forms.first
      login_form['user_mail'] = @user_name
      login_form['user_password'] = @password
      @agent.submit(login_form)
    end

    def write (diary)
      edit_page = @agent.get("http://gree.jp/?mode=blog&act=entry_edit")
      edit_form = edit_page.forms.first

      edit_form["title"] = diary.title
      edit_form["description"] = diary.body

      diary.photo_file.each_with_index {|item, index|
        edit_form.file_uploads.name("user_pics[]")[index].file_name =
          item.gsub(/\//, "\\")
      }

      ok_button = edit_form.buttons.name("doConfirm")

      confirm_page = @agent.submit(edit_form, ok_button)
      confirm_form = confirm_page.forms.first
      commit_button = confirm_form.buttons.name("doCommit")

      @agent.submit(confirm_form, commit_button)

      file_base = confirm_form["blog_date"].delete("^0-9")
    end

    class << self
      def make_upimage (cfg)
        images = []
        Dir.chdir(cfg.upfile_dir)
        Dir.entries('.').select {|i|
          i.downcase.match(/.*\.jpg/)
        }[0..2].sort.each_with_index{|item, index|
          up_file = "up#{index}.jpg"
          if cfg.photo_resize
            system("convert -geometry 640x640 -quality 80 #{item} #{up_file}")
          else
            FileUtils.cp(item, up_file)
          end
          FileUtils.mv(item, File.join(cfg.gree_writer_dir, 'orig_images'))
          images.push(File.expand_path(up_file))
        }
        images
      end
    end
  end
end

def main (argv)
  cfg_file = argv[0] ? argv[0] : 'gree_writer.ini'
  cfg = GreeWriter::Config.load(cfg_file)
  diary_writer = GreeWriter::DiaryWriter.new(cfg)
  diary_writer.login

  images = GreeWriter::DiaryWriter.make_upimage(cfg)
  diary = GreeWriter::Diary.new(File.join(cfg.upfile_dir, 'up.txt'), images)
  time_stamp = diary_writer.write(diary)
  diary.move_diary(cfg.gree_writer_dir, time_stamp)
end

main(ARGV)

まだ気に入らないところもあるけど、ひとまずこれで完成ということにするよ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070219

2007-02-16

[] GREEダイアリーライターリファクタリング(1)  GREEダイアリーライターのリファクタリング(1) - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  GREEダイアリーライターのリファクタリング(1) - バリケンのRuby日記  GREEダイアリーライターのリファクタリング(1) - バリケンのRuby日記 のブックマークコメント

昨日のGREEダイアリーライター(設定ファイル版)を、ちょっとずつリファクタリングしていくことにするよ。

まずはgree_writer.iniだよ。

{
  :user_name => '(ユーザ名)', # GREEのユーザー名(登録メールアドレス)
  :password => '(パスワード)', # GREEのパスワード
  :gree_writer_dir => '.', # アップロード済みファイルや元画像ファイルを保管するフォルダ
  :upfile_dir => '.', # アップロードするテキストや画像が置かれたフォルダ
  :photo_resize => false, # アップロードする画像を640ピクセルに変換するかどうか
}

次に、gree_writer.rbだよ。

$KCODE = 's'

require 'mechanize'
require 'kconv'
require 'fileutils'

module GreeWriter
  class Config
    def initialize (config = {})
      @user_name = config[:user_name]
      @password = config[:password]
      @gree_writer_dir = File.expand_path(config[:gree_writer_dir])
      @upfile_dir = File.expand_path(config[:upfile_dir])
      @photo_resize = config[:photo_resize]
    end
    attr_reader :user_name, :password, :gree_writer_dir, :upfile_dir, :photo_resize

    def self.load (file)
      self.new(eval(File.open(file).read))
    end
  end

  class DiaryWriter
    def initialize (user, pass)
      @user_name = user
      @password = pass
      @agent = WWW::Mechanize.new
    end

    private
    def read_uptext (file)
      body = open(file).read.to_a
      title = body.shift.chomp.toeuc
      body = body.join.toeuc
      [title, body]
    end

    def make_save_directory (dir)
      orig_images_dir = File.join(dir, 'orig_images')
      uploaded_dir = File.join(dir, 'uploaded')

      raise if File.exist?(orig_images_dir) and not File.directory?(orig_images_dir)
      raise if File.exist?(uploaded_dir) and not File.directory?(uploaded_dir)

      FileUtils.mkdir(orig_images_dir) unless File.exist?(orig_images_dir)
      FileUtils.mkdir(uploaded_dir) unless File.exist?(uploaded_dir)
    end

    def login
      login_page = @agent.get("http://gree.jp/")
      login_form = login_page.forms.first
      login_form['user_mail'] = @user_name
      login_form['user_password'] = @password
      @agent.submit(login_form)
    end

    def edit_diary (gree_writer_dir, upfile_dir, photo_resize)
      upfile = File.join(upfile_dir, 'up.txt')
      title, body = read_uptext(upfile)

      edit_page = @agent.get("http://gree.jp/?mode=blog&act=entry_edit")
      edit_form = edit_page.forms.first
      edit_form["title"] = title
      edit_form["description"] = body

      Dir.chdir(upfile_dir)

      Dir.entries('.').select {|i|
        i.downcase.match(/.*\.jpg/)
      }[0..2].sort.each_with_index{|item, index|
        if photo_resize
          system("convert -geometry 640x640 -quality 80 #{item} up#{index}.jpg")
        else
          FileUtils.cp(item, "up#{index}.jpg")
        end
        FileUtils.mv(item, File.join(gree_writer_dir, 'orig_images'))
      }

      files = Dir.entries('.').select {|i|
        i.downcase.match(/up.*\.jpg/)
      }.map {|i|
        Dir.pwd.gsub(/\//, "\\") + "\\" + i
      }

      files.each_with_index {|item, index|
        edit_form.file_uploads.name("user_pics[]")[index].file_name = item
      }

      ok_button = edit_form.buttons.name("doConfirm")

      confirm_page = @agent.submit(edit_form, ok_button)
      confirm_form = confirm_page.forms.first
      commit_button = confirm_form.buttons.name("doCommit")

      @agent.submit(confirm_form, commit_button)

      file_base = confirm_form["blog_date"].delete("^0-9")
      File.rename(upfile, File.join(gree_writer_dir, 'uploaded', file_base + ".txt"))

      files.each_with_index {|item, index|
        File.rename(item, File.join(gree_writer_dir, 'uploaded', file_base + "_#{index}.jpg"))
      }
    end

    public
    def write (gree_writer_dir, upfile_dir, photo_resize)
      make_save_directory(gree_writer_dir)
      login
      edit_diary(gree_writer_dir, upfile_dir, photo_resize)
    end
  end
end

def main (argv)
  cfg_file = argv[0] ? argv[0] : 'gree_writer.ini'
  cfg = GreeWriter::Config.load(cfg_file)
  diary_writer = GreeWriter::DiaryWriter.new(cfg.user_name, cfg.password)

  diary_writer.write(cfg.gree_writer_dir, cfg.upfile_dir, cfg.photo_resize)
end

main(ARGV)

うーん、まだedit_diaryメソッドの中がゴチャゴチャしてるから、もうちょっとスッキリさせたいねえ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070216

2007-02-15

[] GREEダイアリーライター(iniファイル利用版)  GREEダイアリーライター(iniファイル利用版) - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  GREEダイアリーライター(iniファイル利用版) - バリケンのRuby日記  GREEダイアリーライター(iniファイル利用版) - バリケンのRuby日記 のブックマークコメント

こないだのGREEダイアリーライターをちょっと改良して、各自で設定する部分はiniファイルとして分離したバージョンを作ってみたよ(Windows版)。

次のようなgree_writer.iniを、gree_writer.rbと同じフォルダに用意してね。

{
  :user_name => '(ユーザ名)', # GREEのユーザー名(登録メールアドレス)
  :password => '(パスワード)', # GREEのパスワード
  :photo_resize => false, # アップロードする画像を640ピクセルに変換するかどうか
  :upfile_dir => '.', # アップロードするテキストや画像が置かれたフォルダ
}

改良版のgree_writer.rbは、次のとおりだよ。

$KCODE = 's'

require 'mechanize'
require 'kconv'
require 'fileutils'

module GreeWriter
  class Config
    def initialize (config = {}) 
      @user_name = config[:user_name]
      @password = config[:password]
      @photo_resize = config[:photo_resize]
      @upfile_dir = config[:upfile_dir]
    end
    attr_reader :user_name, :password, :photo_resize, :upfile_dir

    def self.load (filename)
      self.new(eval(File.open(filename).read))
    end
  end

  class DiaryWriter
    def initialize (config)
      @user_name = config.user_name
      @password = config.password
      @photo_resize = config.photo_resize
      @upfile_dir = config.upfile_dir

      @body = open(File.join(@upfile_dir, 'up.txt')).read.to_a
      @title = @body.shift.chomp.toeuc
      @body = @body.join.toeuc

      raise if File.exist?('orig_images') and not File.directory?('orig_images')
      raise if File.exist?('uploaded') and not File.directory?('uploaded')

      FileUtils.mkdir('orig_images') unless File.exist?('orig_images')
      FileUtils.mkdir('uploaded') unless File.exist?('uploaded')
    end

    def write
      current_dir = Dir.pwd
      agent = WWW::Mechanize.new
      gree_page = agent.get("http://gree.jp/")

      login_form = gree_page.forms.first
      login_form['user_mail'] = @user_name
      login_form['user_password'] = @password
      agent.submit(login_form)

      edit_page = agent.get("http://gree.jp/?mode=blog&act=entry_edit")

      edit_form = edit_page.forms.first
      edit_form["title"] = @title
      edit_form["description"] = @body

      Dir.chdir(@upfile_dir)

      Dir.entries('.').select {|i|
        i.downcase.match(/.*\.jpg/)
      }[0..2].sort.each_with_index{|item, index|
        if @photo_resize
          system("convert -geometry 640x640 -quality 80 #{item} up#{index}.jpg")
        else
          FileUtils.cp(item, "up#{index}.jpg")
        end
        FileUtils.mv(item, File.join(current_dir, 'orig_images'))
      }

      files = Dir.entries('.').select {|i|
        i.downcase.match(/up.*\.jpg/)
      }.map {|i|
        Dir.pwd.gsub(/\//, "\\") + "\\" + i
      }

      files.each_with_index {|item, index|
        edit_form.file_uploads.name("user_pics[]")[index].file_name = item
      }

      ok_button = edit_form.buttons.name("doConfirm")

      confirm_page = agent.submit(edit_form, ok_button)

      confirm_form = confirm_page.forms.first
      commit_button = confirm_form.buttons.name("doCommit")

      agent.submit(confirm_form, commit_button)

      file_base = confirm_form["blog_date"].delete("^0-9")
      File.rename('up.txt', File.join(current_dir, 'uploaded', file_base + ".txt"))

      files.each_with_index {|item, index|
        File.rename(item, File.join(current_dir, 'uploaded', file_base + "_#{index}.jpg"))
      }
    end
  end
end

def main (argv)
  config_file = argv[0] ? argv[0] : 'gree_writer.ini'
  config = GreeWriter::Config.load(config_file)

  diary_writer = GreeWriter::DiaryWriter.new(config)
  diary_writer.write
end

main(ARGV)

これをgree_writer.rbとして保存してね。

こうして設定ファイルを別に用意する形式なら、RubyScript2Exeなんかを使ってexeファイルにして、ほかの人に使ってもらうこともできるよね。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070215

2007-02-12

[] Gmailメールを送る  Gmailでメールを送る - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Gmailでメールを送る - バリケンのRuby日記  Gmailでメールを送る - バリケンのRuby日記 のブックマークコメント

またmechanizeの練習として、こんどはテキストファイルで書いた文章をGmailで送るスクリプトを作ってみたよ(Windows版)。ちなみにWindows以外でも「$KCODE = 's'」を「$KCODE = 'e'」か「$KCODE = 'u'」にすれば多分動くと思うよ。

$KCODE = 's'

USERNAME = '(ユーザ名)'
PASSWORD = '(パスワード)'

require 'mechanize'
require 'kconv'
require 'fileutils'

body = open('up.txt').read.to_a
to = body.shift.chomp
cc = body.shift.chomp
bcc = body.shift.chomp
subject = body.shift.chomp
body = body.join

agent = WWW::Mechanize.new
gmail_page = agent.get('http://mail.google.com/mail/h/')

login_form = gmail_page.forms.first
login_form['Email'] = USERNAME
login_form['Passwd'] = PASSWORD

agent.submit(login_form)

agent.get('http://mail.google.com/mail/h/')

gmail_page = agent.get('?v=b&pv=tl&cs=b')

mail_form = gmail_page.form('f')

mail_form['to'] = to
mail_form['cc'] = cc
mail_form['bcc'] = bcc
mail_form['subject'] = subject.toutf8
mail_form['body'] = body.toutf8

files = ARGV.map do |i|
  File.expand_path(i)
end

if ARGV[0]
  raise if not File.exist?(files[0]) or File.directory?(files[0])
  mail_form.file_uploads.name('file0').first.file_name = files[0]
end

send_button = mail_form.buttons.name('nvp_bu_send')

agent.submit(mail_form, send_button)

これをgmail_sender.rbとして保存してね。スクリプト中の「(ユーザ名)」と「(パスワード)」は、各自の環境に合わせて編集してね。あ、もちろんスクリプト中に生パスワードを書くことになるから、このファイルの扱いには十分注意してね。

使い方

gmail_sender.rbが置かれたフォルダに、up.txtというテキストファイルを作ってね。ファイルは、1行目が「To:」フィールド、2行目が「Cc:」フィールド、3行目が「Bcc:」フィールド、4行目がメールタイトル、5行目以降がメールの本文になるよ。複数のメールアドレスを指定したいときは、カンマで区切ってね。たとえば、up.txtを次のようにすると、

hoge@example.com, fuga@example.com
foo@example.com
bar@example.com
メール送信のテスト
Gmailでメールを送ってみるテストです。

ちゃんと届きましたか?

「To:」が「hoge@example.com, fuga@example.com」、「Cc:」が「foo@example.com」、「Bcc:」が「bar@example.com」、メールタイトルが「メール送信のテスト」になるよ。

Cc:」や「Bcc:」フィールドが不要なら、2行目と3行目は空行(改行だけの行)にしてね。

あとは、

> ruby gmail_sender.rb

で、up.txtの内容でメールが送信できるよ。さらに

> ruby gmail_sender.rb [添付ファイル名]

で、「[添付ファイル名]」で指定したファイルを添付して送ることができるよ。いまのところ、手抜きして添付ファイルは1つしか付けられない仕様で作っちゃったから、複数ファイルを送りたいときはアーカイブして送ってね。

garyogaryo2007/02/23 11:23こんにちは。こちらのエントリーを参考にPRagger用Gmail送信プラグイン作ってみました。PRaggerの開発元に送ってもいいですか?http://ruby.g.hatena.ne.jp/garyo/20070223/1172196852

muscovyduckmuscovyduck2007/02/23 11:41garyoさん>
日記中のソースコードは、ライセンスは明示していませんが、Rubyライセンス準拠でどうぞご自由にご利用ください!
http://www.ruby-lang.org/ja/LICENSE.txt

garyogaryo2007/02/23 11:43ありがとうございます。

muda_kskmuda_ksk2007/09/06 00:08昨日こちらのスクリプトを使用させてもらおうとしたのですが、mail_form = gmail_page.form('f')でエラーがでました。
agent.get('http://mail.google.com/mail/h/')
gmail_page = agent.get('?v=b&pv=tl&cs=b')
これを
gmail_page = agent.get('http://mail.google.com/mail/h/?v=b&pv=tl&cs=b')
とするとスクリプトは終わったようにみえるのですが、
送信できませんでした。
サポートされないブラウザとして扱われているようです。

muscovyduckmuscovyduck2007/09/06 07:22muda_kskさん>
Gmailの仕様が変わったのかもしれませんね。
agent = WWW::Mechanize.newの次の行に
agent.user_agent_alias = 'Windows IE 6'
とか入れるとどうでしょうか?

muda_kskmuda_ksk2007/09/06 21:49user agent設定してみましたが、送信されないようです。
ブラウザで簡易HTMLモードを試していると、
http://mail.google.com/mail/h/ランダムな文字/?v=b&pv=tl&cs=b
のようになっていてセッション管理でもしているのではないかと思われます。
また、ログインボタンを押した後のページ遷移ではMetaタグで行き先を指定されるよう、
なのですが、
follow_meta_refresh = ture
などとするとuriのパースで例外が出てしまいます。
なかなか手ごわい感じです。

2007-02-11

[] GREEダイアリーライター  GREEダイアリーライター - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  GREEダイアリーライター - バリケンのRuby日記  GREEダイアリーライター - バリケンのRuby日記 のブックマークコメント

mechanizeの練習として、テキストファイルで書いた文章をGREE日記に書き込む「GREEダイアリーライター」を作ってみたよ(Windows版)。もちろんRubyインストールされていないと使えないから、こちらからruby185-22.exeをダウンロードしてインストールしておいてね。

Ruby本体以外にも、hpricotライブラリとmechanizeライブラリ、あとWindows版のImageMagickが必要だよ。

> gem install hpricot
> gem install mechanize

でhpricotとmechanizeをインストールして、さらにここからImageMagick-6.3.2-4-Q16-windows-static.exeをダウンロードしてインストールしておいてね。

$KCODE = 's'

USERNAME = "(ユーザ名)"
PASSWORD = "(パスワード)"
PHOTORESIZE = true

require 'mechanize'
require 'kconv'
require 'fileutils'

body = open("up.txt").read.to_a
title = body.shift.chomp
body = body.join

raise if File.exist?('orig_images') and not File.directory?('orig_images')
raise if File.exist?('uploaded') and not File.directory?('uploaded')

FileUtils.mkdir('orig_images') unless File.exist?('orig_images')
FileUtils.mkdir('uploaded') unless File.exist?('uploaded')

agent = WWW::Mechanize.new
gree_page = agent.get("http://gree.jp/")

login_form = gree_page.forms.first
login_form['user_mail'] = USERNAME
login_form['user_password'] = PASSWORD
agent.submit(login_form)

edit_page = agent.get("http://gree.jp/?mode=blog&act=entry_edit")

edit_form = edit_page.forms.first
edit_form["title"] = title.toeuc
edit_form["description"] = body.toeuc

Dir.entries('.').select {|i|
  i.downcase.match(/.*\.jpg/)
}[0..2].sort.each_with_index{|item, index|
  if PHOTORESIZE
    system("convert -geometry 640x640 -quality 80 #{item} up#{index}.jpg")
  else
    FileUtils.cp(item, "up#{index}.jpg")
  end
  FileUtils.mv(item, 'orig_images')
}

files = Dir.entries('.').select {|i|
  i.downcase.match(/up.*\.jpg/)
}.map {|i|
  Dir.pwd.gsub(/\//, "\\") + "\\" + i
}

files.each_with_index {|item, index|
  edit_form.file_uploads.name("user_pics[]")[index].file_name = item
}

ok_button = edit_form.buttons.name("doConfirm")

confirm_page = agent.submit(edit_form, ok_button)

confirm_form = confirm_page.forms.first
commit_button = confirm_form.buttons.name("doCommit")

agent.submit(confirm_form, commit_button)

file_base = confirm_form["blog_date"].delete("^0-9")
File.rename("up.txt", 'uploaded' + "\\" + file_base + ".txt")

files.each_with_index {|item, index|
  File.rename(item, 'uploaded' + "\\" + file_base + "_#{index}.jpg")
}

これをgree_writer.rbとして保存してね。スクリプト中の「(ユーザ名)」と「(パスワード)」は、各自の環境に合わせて編集してね。あ、もちろんスクリプト中に生パスワードを書くことになるから、このファイルの扱いには十分注意してね。

GREEダイアリーライターの使い方

gree_writer.rbが置かれたフォルダに、up.txtというテキストファイルを作ってね。ファイルは、1行目が日記タイトル、2行目以降が日記本文になるよ。

拡張子jpgファイルを同じフォルダにおいておくと、自動的に画像アップロードされるよ。画像ファイルは3つまで自動でアップロードされるよ。3つ以上のjpgファイルを置いても、4つめ以降はアップロードされないでそのフォルダに残っちゃうから注意してね。

アップロードする画像は、ImageMagickで自動的に640x640ピクセル以内にリサイズするよ。元々の画像が640x640ピクセル以内の写真しかアップロードしない人は、リサイズの必要がないから、「PHOTORESIZE = true」を「PHOTORESIZE = false」に変更してね。このあたりは、将来的には自動で判別するようにしたほうがよさそうだねえ。

日記の書き込みが終了すると、元の画像ファイルはorig_imagesフォルダに、縮小画像ファイルとup.txtは書き込んだ日付のファイル名にリネームされてuploadedフォルダにそれぞれ移動されるよ。

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070211

2007-02-07

[][] 青空文庫リンク解析のつづき  青空文庫のリンク解析のつづき - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  青空文庫のリンク解析のつづき - バリケンのRuby日記  青空文庫のリンク解析のつづき - バリケンのRuby日記 のブックマークコメント

前回調べた「作品一覧」のページのうち、とりあえず一番最初のページを対象として、「作品名」と「その作品の図書カードのURL」を配列として返すスクリプトを作ってみたよ。

$KCODE = 's'

require 'open-uri'
require 'nkf'
require 'hpricot'
require 'pp'

index_page = Hpricot.parse(
  NKF.nkf('-s',
    open("http://www.aozora.gr.jp/index_pages/sakuhin_a1.html").read
  )
); nil

pp index_page.search("center table:nth-child(1) tr td a").map {|e|
  [e.inner_text, e['href']]
}

実行結果だよ。

[["ああ華族様だよ と私は嘘を吐くのであった", "../cards/000020/card2569.html"],
 ["ア、秋", "../cards/000035/card236.html"],
 ["ああ玉杯に花うけて", "../cards/000575/card3585.html"],
 ["RUR", "../cards/001236/card46345.html"],
 ["愛", "../cards/000076/card4996.html"],
 ["愛", "../cards/000311/card4027.html"],
 ["藍色の蟇", "../cards/000190/card1029.html"],
 ["「愛怨峡」における映画的表現の問題", "../cards/000311/card2776.html"],
 ["I can speak", "../cards/000035/card1572.html"],
 ["愛卿伝", "../cards/000154/card1639.html"],
 ["愛護若", "../cards/000933/card18403.html"],
 ["哀詞序", "../cards/000157/card43504.html"],
 ["合図の旗", "../cards/000311/card3250.html"],
 ["愛する人達", "../cards/000291/card24361.html"],
 ["愛読書の印象", "../cards/000879/card4872.html"],
 ["愛と婚姻", "../cards/000050/card1040.html"],
 ["「愛と死」", "../cards/000311/card2913.html"],
 ["愛と認識との出発", "../cards/000256/card2590.html"],
 ["愛と美について", "../cards/000035/card1578.html"],
 ["愛と平和を理想とする人間生活", "../cards/000311/card3852.html"],
 ["愛の為めに", "../cards/000260/card46587.html"],
 ["愛の問題(夫婦愛)", "../cards/000256/card43128.html"],
 ["愛は神秘な修道場", "../cards/000311/card3074.html"],
 ["あいびき", "../cards/000005/card5.html"],
 ["あいびき", "../cards/001030/card4843.html"],
 ["愛撫", "../cards/000074/card411.html"],
 ["愛よ愛", "../cards/000076/card4554.html"],
 ["アイルランドにおける貧民の子女が、その両親ならびに国家にとっての重荷となることを防止し、かつ社会に対して有用ならしめんとする方法についての私案",
  "../cards/000912/card4268.html"],
 ["アインシュタイン", "../cards/000042/card43074.html"],
 ["アインシュタインの教育観", "../cards/000042/card43075.html"],
 ["青い顔", "../cards/000022/card197.html"],
 ["青い眼の人形", "../cards/000286/card1758.html"],
 ["青木の出京", "../cards/000083/card485.html"],
 ["青草", "../cards/000601/card4088.html"],
 ["青白き夢", "../cards/000012/card198.html"],
 ["「青白き夢」序", "../cards/000826/card18414.html"],
 ["青水仙、赤水仙", "../cards/000096/card937.html"],
 ["「青空語」に寄せて(昭和二年一月号)", "../cards/000074/card43689.html"],
 ["青空同人印象記(大正十五年六月号)", "../cards/000074/card43695.html"],
 ["『青空』のことなど", "../cards/000074/card43691.html"],
 ["青空のリスタート", "../cards/000055/card698.html"],
 ["青田は果なし", "../cards/000311/card4009.html"],
 ["青猫", "../cards/000067/card1768.html"],
 ["青ひげ", "../cards/001134/card43117.html"],
 ["青服の男", "../cards/000260/card1433.html"],
 ["青森", "../cards/000035/card46597.html"],
 ["青森", "../cards/000035/card4357.html"],
 ["赤い煙突", "../cards/000020/card2572.html"],
 ["赤い貨車", "../cards/000311/card1978.html"],
 ["赤い着物", "../cards/000168/card903.html"]]

2007-02-05

[][] 青空文庫の作品一覧のhtmlファイル配列として得る  青空文庫の作品一覧のhtmlファイルを配列として得る - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  青空文庫の作品一覧のhtmlファイルを配列として得る - バリケンのRuby日記  青空文庫の作品一覧のhtmlファイルを配列として得る - バリケンのRuby日記 のブックマークコメント

解説はまたこんど。とりあえずスクリプトだけ貼り付けるよ。

$KCODE = 's'

require 'open-uri'
require 'nkf'
require 'hpricot'
require 'pp'

aozora = Hpricot.parse(
  NKF.nkf('-s',
    open("http://www.aozora.gr.jp/index_pages/index_top.html").read
  )
); nil

link1 = aozora.search("tr td a:nth-child(0)").map {|e|
  e['href']
}.select{|item|
  item.match(/^sakuhin_[a-z]+\d+\.html$/)
}

link2 = link1.map {|item|
  page = Hpricot.parse(
    NKF.nkf('-s',
      open("http://www.aozora.gr.jp/index_pages/" + item).read
    )
  ); nil
  page.search("tr:nth-child(0) td a").map{|e| e['href'] }.uniq
}

pp (link1 + link2).flatten.sort

実行結果だよ。

["sakuhin_a1.html",
 "sakuhin_a2.html",
 "sakuhin_a3.html",
 "sakuhin_a4.html",
 "sakuhin_a5.html",
 "sakuhin_a6.html",
 "sakuhin_a7.html",
 "sakuhin_e1.html",
 "sakuhin_e2.html",
 "sakuhin_ha1.html",
 "sakuhin_ha2.html",
 "sakuhin_ha3.html",
 "sakuhin_ha4.html",
 "sakuhin_ha5.html",
 "sakuhin_ha6.html",
 "sakuhin_ha7.html",
 "sakuhin_ha8.html",
 "sakuhin_he1.html",
 "sakuhin_he2.html",
 "sakuhin_hi1.html",
 "sakuhin_hi2.html",
 "sakuhin_hi3.html",
 "sakuhin_hi4.html",
 "sakuhin_ho1.html",
 "sakuhin_ho2.html",
 "sakuhin_ho3.html",
 "sakuhin_hu1.html",
 "sakuhin_hu2.html",
 "sakuhin_hu3.html",
 "sakuhin_hu4.html",
 "sakuhin_hu5.html",
 "sakuhin_hu6.html",
 "sakuhin_i1.html",
 "sakuhin_i2.html",
 "sakuhin_i3.html",
 "sakuhin_i4.html",
 "sakuhin_ka1.html",
 "sakuhin_ka2.html",
 "sakuhin_ka3.html",
 "sakuhin_ka4.html",
 "sakuhin_ka5.html",
 "sakuhin_ka6.html",
 "sakuhin_ka7.html",
 "sakuhin_ka8.html",
 "sakuhin_ke1.html",
 "sakuhin_ke2.html",
 "sakuhin_ke3.html",
 "sakuhin_ke4.html",
 "sakuhin_ki1.html",
 "sakuhin_ki2.html",
 "sakuhin_ki3.html",
 "sakuhin_ki4.html",
 "sakuhin_ki5.html",
 "sakuhin_ko1.html",
 "sakuhin_ko2.html",
 "sakuhin_ko3.html",
 "sakuhin_ko4.html",
 "sakuhin_ko5.html",
 "sakuhin_ko6.html",
 "sakuhin_ko7.html",
 "sakuhin_ku1.html",
 "sakuhin_ku2.html",
 "sakuhin_ku3.html",
 "sakuhin_ma1.html",
 "sakuhin_ma2.html",
 "sakuhin_me1.html",
 "sakuhin_me2.html",
 "sakuhin_mi1.html",
 "sakuhin_mi2.html",
 "sakuhin_mo1.html",
 "sakuhin_mo2.html",
 "sakuhin_mu1.html",
 "sakuhin_mu2.html",
 "sakuhin_na1.html",
 "sakuhin_na2.html",
 "sakuhin_ne1.html",
 "sakuhin_ni1.html",
 "sakuhin_ni2.html",
 "sakuhin_ni3.html",
 "sakuhin_nn1.html",
 "sakuhin_no1.html",
 "sakuhin_nu1.html",
 "sakuhin_o1.html",
 "sakuhin_o2.html",
 "sakuhin_o3.html",
 "sakuhin_o4.html",
 "sakuhin_o5.html",
 "sakuhin_ra1.html",
 "sakuhin_re1.html",
 "sakuhin_ri1.html",
 "sakuhin_ro1.html",
 "sakuhin_ru1.html",
 "sakuhin_sa1.html",
 "sakuhin_sa2.html",
 "sakuhin_sa3.html",
 "sakuhin_sa4.html",
 "sakuhin_se1.html",
 "sakuhin_se2.html",
 "sakuhin_se3.html",
 "sakuhin_se4.html",
 "sakuhin_si1.html",
 "sakuhin_si10.html",
 "sakuhin_si11.html",
 "sakuhin_si12.html",
 "sakuhin_si13.html",
 "sakuhin_si2.html",
 "sakuhin_si3.html",
 "sakuhin_si4.html",
 "sakuhin_si5.html",
 "sakuhin_si6.html",
 "sakuhin_si7.html",
 "sakuhin_si8.html",
 "sakuhin_si9.html",
 "sakuhin_so1.html",
 "sakuhin_so2.html",
 "sakuhin_su1.html",
 "sakuhin_su2.html",
 "sakuhin_ta1.html",
 "sakuhin_ta2.html",
 "sakuhin_ta3.html",
 "sakuhin_ta4.html",
 "sakuhin_ta5.html",
 "sakuhin_te1.html",
 "sakuhin_te2.html",
 "sakuhin_ti1.html",
 "sakuhin_ti2.html",
 "sakuhin_ti3.html",
 "sakuhin_to1.html",
 "sakuhin_to2.html",
 "sakuhin_to3.html",
 "sakuhin_to4.html",
 "sakuhin_tu1.html",
 "sakuhin_tu2.html",
 "sakuhin_u1.html",
 "sakuhin_u2.html",
 "sakuhin_u3.html",
 "sakuhin_wa1.html",
 "sakuhin_wa2.html",
 "sakuhin_wa3.html",
 "sakuhin_wo1.html",
 "sakuhin_ya1.html",
 "sakuhin_ya2.html",
 "sakuhin_yo1.html",
 "sakuhin_yo2.html",
 "sakuhin_yu1.html",
 "sakuhin_yu2.html",
 "sakuhin_zz1.html"]
トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20070205

2007-02-02

[] mechanize  mechanize - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  mechanize - バリケンのRuby日記  mechanize - バリケンのRuby日記 のブックマークコメント

mechanizeというライブラリを使うと、「自動でWebの巡回をするツール」をRubyで作ることができるみたい。

WWW::Mechanizeでできること

Greenbear Laboratory - Ruby's WWW::Mechanize 日本語リファレンス

面白そう!mechanizeはhpricotというライブラリに依存しているから、インストールは

# gem install hpricot --source http://code.whytheluckystiff.net
# gem install mechanize

とすればいいみたい。

hpricotというライブラリは、scrAPIと似たようなライブラリみたいだよ。こっちも使い方を調べてみなくっちゃ。

追記:RubyForgeにhpricotの更新が反映されたみたいだから、

# gem install hpricot

でOKみたい。

2007-02-01

[] net/ftp  net/ftp - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  net/ftp - バリケンのRuby日記  net/ftp - バリケンのRuby日記 のブックマークコメント

Rubyの標準ライブラリのひとつ、net/ftpを使うと、ftpを使ったファイル転送Rubyで簡単に実現できるよ。

たとえば、いままでftpコマンドを使って

> ftp ftpサーバ名
Connected to ftpサーバ名.
220 Hello. FTP server ready.
User (ftpサーバ名:(none)): ユーザー名
331 Password required for ユーザー名.
Password: パスワード
230 User ユーザー名 logged in.
ftp> bin
200 Type set to I
ftp> cd public
250 CWD command successful
ftp> get index.html
200 PORT command successful
150 Opening BINARY mode data connection for index.html (92 bytes)
226 Transfer complete.
ftp: 92 bytes received in 0.02Seconds 4.60Kbytes/sec.
ftp> quit
221 Goodbye.

>

のようにしていたとしたら、

require 'net/ftp'

ftp = Net::FTP.new
ftp.connect('ftpサーバ名')
ftp.login('ftpユーザー名', 'パスワード')
ftp.binary = true
ftp.chdir('public')
ftp.get('index.html')
ftp.quit

という感じでRubyスクリプトとして書くことができるよ。

じゃあ、Windowsのログオン/ログオフスクリプトを使って、自分のオンライン情報Webサーバftp転送するスクリプトを作ってみるよ。

まずは、ログオン時に起動されるonline.rbだよ。あらかじめftp先にstatusという名前のディレクトリを作っておいてね。あと、スクリプト中にパスワードを直接書いているから、その危険性は十分理解して使ってね。

require 'net/ftp'

ftp = Net::FTP.new
ftp.connect('ftpサーバ名')
ftp.login('ftpユーザー名', 'パスワード')
ftp.binary = true
ftp.chdir('status')
ftp.put('online.html', 'index.html')
ftp.quit

online.htmlは、次のようにしてね。

<html>
<head>
<title>I'm online!</title>
</head>
<body>
I'm online!
</body>
</html>

次に、ログオフ時に起動されるoffline.rbだよ。

require 'net/ftp'

ftp = Net::FTP.new
ftp.connect('ftpサーバ名')
ftp.login('ftpユーザー名', 'パスワード')
ftp.binary = true
ftp.chdir('status')
ftp.put('offline.html', 'index.html')
ftp.quit

offline.htmlは、次のようにしてね。

<html>
<head>
<title>I'm offline!</title>
</head>
<body>
I'm offline!
</body>
</html>

そして、online.rbをログオンスクリプトに、offline.rbをログオフスクリプトに指定すれば、「http://サーバ名/status/」にアクセスしてもらえば、自分のオンライン状態をチェックしてもらえるよ。