トップ 最新 追記

Route 477



2007-02-05

[ruby] RubyでHTMLとWebを操作するためのライブラリ、HpricotとWWW::Mechanize

今日は、RubyでWebサイトを解析するときに強い味方となるライブラリ、HpricotとWWW::Mechanizeを紹介します。

どちらも非常に強力なので、覚えておいて損はないよ!

以下ではまずHpricotでHTMLを解析・編集する方法について解説します。 次に、「はてなダイアリーの自動更新」を例にWWW::Mechanizeの使い方を解説します。

Hpricot

HpricotはHTMLを解析するためのライブラリです。

例えば「あるページのリンクだけを全部抜き出したい」と思ったとき、どうしますか?scrAPIを使う?でもscrAPIはやっぱり ちょっと使いたいだけなのにパーザ(Scrape)用のクラスを定義するのが面倒なんだよね!

Hpricotなら、たったこれだけで解析完了です。

require 'hpricot'
require 'open-uri'

doc = Hpricot( open("http://www.kmc.gr.jp/").read )

(doc/:a).each do |link|
  puts "#{link.inner_html} → #{link[:href]}"
end

HTMLを読み込むには「Hpricot()」という関数にHTMLデータの文字列を与えればOK。 例ではOpenURIを使ってネットから取得しています。

タグを検索するには、読み込んだデータの「/」というメソッドにシンボルか文字列を渡します。

tableタグを全て探す
doc/:table
<div id="hoge">を全て探す
doc/"div#hoge"
<span class="moge">を全て探す
doc/"span.moge"

CSSに慣れている人なら気付いたと思いますが、2番目、3番目の書き方は「CSSセレクタ」と呼ばれるものです。 Hpricotでは他にもさまざまなCSSセレクタを 使用することができます。*1

<table id="list">の中のtdタグを全て探す
doc/"table#list td"
"index.cgi?"から始まるリンクを全て探す
doc/"a[@href^='index.cgi?']"
テキストに"new"という文字列が入っているリンクを全て探す(※version 0.5以降)
doc/"a[text()*='new']"

上の例はどれも「〜なタグを全て探す」でしたが、条件に当てはまるものを一つだけ取り出したいこともありますよね。 そんな時はdoc.at()が使えます。

一番最初のリンクを探す
doc.at(:a)
ElementsとElem

タグ一覧はHpricot::Elementsのインスタンスになります。これはArrayのサブクラスなので、 firstで最初の要素を取り出したり、eachでループを回したり、find_allで条件に合うものだけ抜き出したりできます。

またそれぞれのタグはHpricot::Elemのインスタンスになります。

Elemには以下のようなメソッドが定義されています。

  • attributes : 属性一覧
  • ["href"] : 属性hrefの値を得る
  • name : タグ名
  • parent : 親要素
  • containers : 子要素(タグのみ)
  • children : 子要素(テキストやコメントも含む)
  • css_path : その要素のCSSパス
  • previous_sibling, next_sibling : 隣の要素(タグのみ)
  • previous_node, next_node : 隣の要素(テキストやコメントも含む)
  • each_child{|elem| ..} : 子要素について繰り返す
  • inner_html, inner_text : 内部のHTML, 内部のテキスト
  • to_html : そのタグも含めたHTML
  • to_original_html : そのタグも含めたHTML(なるべく元のHTMLと同じように出力する)
  • to_plain_text : 読みやすいテキストに整形する

実はElemやElementsにも「/」や「at」が定義されているので、「/」による検索結果に対し「その中をさらに探す」なんてこともできます。

例:

divタグの中のリンクを全て探す
doc/:div/:a
各divタグの中の最初のリンクを探す
(doc/:div).each do |div|
  first_link = div/:a
end
HTMLに変更を加える

Hpricotの凄いところはHTMLへの変更もサポートしている点です。 linkをあるリンクタグだとすると、link.inner_html に文字列を代入すると中身のHTMLが変更されたり、 link[:href] = "foo.html" とするとリンク先が"foo.html"になったりします。

これらの変更は、to_htmlなどが生成するHTMLに反映されます。 例えば「サーバ移転したのでexample.jpへのリンクをexample.orgに直したい」という場合は、

doc = Hpricot(ARGF.read)
(doc/:a).each do |link|
  link[:href].gsub! %r(http://example.jp/), "http://example.org"
end
print doc.to_html

という感じで簡単にフィルタスクリプトを書くことができます。

変更系のメソッドには以下のようなものがあります。

  • ["href"]= : 属性hrefの値をセットする
  • inner_html= : 内部のHTMLをセットする
  • name= : タグ名をセットする
  • children= : 子要素をセットする
余談

「/」とか個性的なメソッド名が多いHpricotですが、実は「もっと普通の名前」も用意されています。 例えば「/」には「search」という別名がありますし、 HTMLの読み込みは Hpricot.parse(html) とも書けます。 でもやっぱり短い方を使っちゃうんだよね…!(便利だから)

WWW::Mechanize

WWW::Mechanizeは、Webサイトへのアクセスを自動化するためのライブラリです。

例えばはてなダイアリーに日記を投稿するスクリプトを書くとしましょう。 Net::HTTPだとまず投稿用のURLを調べて、フォームタグの名前を調べてPOSTして、あれ認証ってどうやるんだっけ…?なんて いろいろなことを考えないといけませんが、WWW::Mechanizeを使えば「普段ブラウザを操作するのと同じような感覚で」 スクリプトを書くことができます。

では、ブラウザから日記を書くときの手順を思い出してみましょう。

  1. http://d.hatena.ne.jp/(ユーザid)/ を開く
  2. おおっと、ログインしてなかった。右上の「ログイン」をクリックする
  3. ユーザー名とパスワードを入力し、「ログイン」を押す
  4. 「自動でページを移動しています(移動しないときはこちらのリンクを…)」
  5. 「日記を書く」をクリック
  6. textareaに日記本文を入力し、「この内容を登録する」を押す

WWW::Mechanizeなら、これをそのままスクリプトに落とすことができます。

(1) http://d.hatena.ne.jp/(ユーザid)/ を開く
 require 'mechanize'
 require 'kconv' #あとでUTF-8を扱うので

 agent = WWW::Mechanize.new
 diary_page = agent.get("http://d.hatena.ne.jp/(自分のはてなid)/")

newでインスタンスを作って、getでWebページを取得しています。簡単ですね。

(2) おおっと、ログインしてなかった。右上の「ログイン」をクリックする
 login_link = diary_page.links.text("ログイン".toeuc)
 login_page = agent.get(login_link.href)

diary_page.linksでページ中のリンク一覧が取得できます。ここでは、全てのリンクの中から テキストが"ログイン"に一致するものを探しています。

login_linkはMechanize::Linkのインスタンスで、login_link.hrefでURLが得られます。agent.getにこのURLを渡してログインページを開きましょう。

(3) ユーザー名とパスワードを入力し、「ログイン」を押す
 login_form = login_page.forms.first
 login_form['key'] = "(ユーザ名)"
 login_form['password'] = "(パスワード)"
 redirect_page = agent.submit(login_form)

login_page.formsでformタグの一覧が取得できます。ログインページにはformが一つしかないので、firstで最初のformを選んでいます。

次の行では、<input name="key" ...> というインプットボックスにユーザ名を入力しています。 パスワードも同様に入力します。

agent.submitにこのフォームを渡すとフォームの内容が送信され、送信結果のページが返ってきます。

(4) 「自動でページを移動しています(移動しないときはこちらのリンクを…)」
 diary_link = redirect_page.links.text("こちら".toutf8)
 diary_page = agent.get(diary_link.href)

ログインすると、おなじみの「移動しないときはこちらのリンクを…」というリダイレクトページになります。 WWW::Mechanizeにはリダイレクトを自動で追跡してくれる機能がある…のですが、このリダイレクトページは200 OKなので 自動追跡が効きません(´・ω・`) 仕方がないので、手動でリンクをクリックしましょう。 またこのページはさっきとちがってUTF-8なので、"こちら"もUTF-8に変換しておきます。

(5) 「日記を書く」をクリック
 edit_link = diary_page.links.text("日記を書く".toeuc)
 edit_page = agent.get(edit_link.href)

ここまで来たらあと一歩です。"日記を書く"のリンクを探し、クリックします。

(6) textareaに日記本文を入力し、「この内容を登録する」を押す
 edit_form = edit_page.forms.name("edit").first
 edit_form["body"] += "\n*Rubyから日記を更新してみるテスト。"
 ok_button = edit_form.buttons.name("edit")
 agent.submit(edit_form, ok_button)

さあ、いよいよ日記の書き込みです。編集画面には複数のフォームがあるので、edit_page.form("edit") で最初の <form name="edit" ...> というタグを見つけています。また、このフォームには「確認する」と「登録する」という複数のsubmitボタンがあるので、 登録ボタンを探してsubmitに渡しています(「こっちのボタンを押してください」という意味です)。

http://d.hatena.ne.jp/(はてなid)/ を見てみてください。新しい日記が書き込まれましたか?:-)

WWW::Mechanizeではこのように、ブラウザを操作するような感覚でスクリプトを書くことができます。

簡易リファレンス

pageには以下のようなメソッドがあります。

  • links : リンク(aタグ)一覧
  • forms : formタグ一覧
  • form("foo") : name="foo"である最初のformタグ
  • title : ページタイトル(titleタグの中身)
  • header : HTTPのレスポンスヘッダ
  • root : ページの内容を表すHpricotドキュメント

linksやformsはMechanize::Listのインスタンスを返します。これはArrayのサブクラスなので、配列のように扱うことができます。 また簡便のため、「hrefが"index.cgi"であるものを全て探す」という操作を links.href("index.cgi") のように書けたり、 「name属性が"hoge"であるものを全て探す」という操作を forms.name("hoge") と書けるようになっています。

linkには以下のようなメソッドがあります。

  • href : リンク先のURL
  • text : aタグの中身のテキスト
  • node : aタグを表すHpricot::Elem
  • click : リンクをクリックし、結果のページを返す (newpage = agent.get(link.href) が、 newpage = link.click のように書ける)

formには以下のようなメソッドがあります。

  • []=("foo", "bar") : name="foo" であるフィールドに値"bar"をセットする
  • submit : フォームをsubmitし、結果のページを返す (newpage = agent.submit(form) が、 newpage = form.submit のように書ける)

より詳細なリファレンスはWWW::Mechanize 日本語リファレンスを参照してください。

インストール

さて、そろそろ実際に使ってみたくなったでしょうか?:-) rubygemsをインストール済みなら、

gem install hpricot
gem install mechanize

と、簡単なコマンドでインストールできます。

  • 前述のとおりmechanizeはhpricotに依存しているので、 mechanizeを入れればhpricotは自動的に入ります。
  • Hpricotはつい最近version 0.5が出たので、昔インストールしたことがある人もアップデートをお勧めします。
  • Hpricotは途中でUnix版を入れる(ruby)かWindows版を入れる(mswin32)か聞かれるので、自分の使っている方を選んでください。

rubygemsを使っていない人はアーカイブをダウンロードし、中に入っているinstall.rbを実行すればOKです(たぶん)

まとめ

本稿では、RubyでWebから情報を得るときに役立つ2つのライブラリ、HpricotとWWW::Mechanizeを紹介しました。

これらを使うことで、HTMLのスクレイプやWebアクセスの自動化など今まで「面倒そうだなぁ」と思っていた処理が非常に簡単に書けるようになります。 Webでの情報収集を自動化したくなったとき、この2つのライブラリのことを思い出してもらえれば幸いです。

*1 さらにHpricotではXPathも使える…のですが、たいていはCSSセレクタで間に合うと思います。

[ruby][hiki] HikiReload

WWW::Mechanizeの使用例として、「テキストファイルが保存されたら自動的にWikiを更新する」というスクリプトを載せておきます。 上の記事もこのスクリプトを使って下書きしましたが、自分のお気に入りのエディタを使えるので非常に便利でした:-)

フォームタグを名前で探しているのでHiki限定ですが、他のWikiエンジンへの対応も難しくないと思います。 ぜひチャレンジしてみてください。

#!/usr/bin/env ruby
require "mechanize"

class HikiReload
  POLL_SECONDS = 3

  def initialize(url)
    @agent = WWW::Mechanize.new
    @url = url
  end

  def poll(path)
    loop do
      mtime = File.mtime(path)
      while File.mtime(path) == mtime
        sleep POLL_SECONDS
      end
      puts "file #{path} changed.."
      submit(File.read(path))
      puts "submitted."
    end
  end

  def submit(str)
    editpage = @agent.get(@url)
    form = editpage.forms.first
    form.contents = str
    result_page = @agent.submit(form, form.buttons.name("save"))
    puts result_page.root.to_html
  end
end

url, file = "http://example.jp/hiki/?c=edit;p=FrontPage", "temp.txt"

HikiReload.new(url).poll(file)

使い方

  1. hikireload.rbという名前で保存(urlは自分のWikiに書き換える)
  2. temp.txtに下書きを書く
  3. ruby hikireload.rbで起動
  4. temp.txtを編集すると、エディタで保存するたびにWikiが更新される

エディタでWikiを編集するためのソフトウェアはemacsのhiki-modeなどいくつかありますが、このアプローチだと エディタを選ばない(emacsでもVimでもxyzzyでも秀丸でもなんでも使える)のが利点だと思います。

本日のツッコミ(全3件) [ツッコミを入れる]

 [すごいですね。ruby だんだん好きになっちゃいましたよ。 ruby中毒になった感じ]

ザッピング運営事務局 [※このコメントは、 ザッピングをご利用して頂いているユーザー様を対象にお送りしております。 既にタグを変更して頂いて..]

ケムス [良く勉強になりました。  ありがとうございます。]


2007-02-06

[prog] C#2.0時代のゲームプログラミング(49) 〜 delegateを用いたcontinuation

C#の無名delegate(クロージャっぽいやつ)とCPS変換の話。

11月ごろから書こうと思いつつ放置しているネタに「call/ccを使ってRPGのイベントを実装する(した)」という話が あるんだけど、そうか、実は

  1. call/ccでやる
  2. yieldでやる
  3. CPS変換でやる

という3つの似た選択肢があるんだな。2とか3だとどういうコードになるんだろ…。あとで考える。


2007-02-09

[web] googleで「○○から××」を検索すると経路検索になる

非常に便利。

google:京都から大阪

google:鹿児島から稚内

google:地球からイスカンダル

あれ、イスカンダルまでの経路は検索できないのか(できても困るが)

[tDiary] スパムうぜー

仕方がないのでとりあえずgmail.comを弾いてみる。

「この日の日記を隠す」にしたのに、その日付へのコメントスパムが止まない。どうなってるんだ。

あと本文に"Drugs sucks"が含まれるのも弾くようにした。俺に言われても困る。

[web] favicon2dotsと実物のdot'sを比べてみるテスト

http://favicon2dots.com/show/5c1b77f8ee9b8e2c2ef68a0b2f2a034b78daeea9

7f8ee9b8e2c2ef68a0b2f2a034b78daeea9.png

http://sorakaze.net/blog/archives/2006/11/post_69.html

20061127_03-thumb.jpg

おお、なかなかそれっぽい。

おまけ

58ab0b5303969e65df1fea19b953bfd8adf.png

部員が作ってくれたDown!!のゲームオーバー画面です。

こんなんが机の上に飾ってあったら非常に嫌だw

[haskell] Liskell - clemens.endorphin.org

Lispの皮をかぶったHaskell、らしい。

  • Haskell … 副作用なし+型推論(+遅延評価)によって安全なプログラミングが可能
  • Lisp … マクロによって柔軟なメタプログラミングが可能

じゃあLispのS式をHaskellに変換したら最強なんじゃね?wwwというプロジェクトのようだ。

実装はS式をHaskellの構文木に変換し、それをGHCに渡すようになっているとのこと(http://clemens.endorphin.org/weblog/archives/2007-01.shtml#e2007-01-31T12_21_00.txt)

非常に面白そうなんだが、LispとHaskellと両方知ってないといけないとか敷居が高いにもほどがある(笑)。

以下は書きかけの論文より、クイックソートの例。

(define (quicksort xs)
  (case xs
    (nil nil)
    ((: x xs) (++ (quicksort (filter (< x) xs))
                  '(x)
                  (quicksort (filter (>= x) xs))))))

うーん、LispのようでLispでなく、パターンマッチや(< x)のあたりにHaskellの匂いが。まさにLiskell。

ところでHaskellでマクロを使いたいっていう需要はどれくらいあるんだろう。

[prog] lispがとっつきにくいという話

Matzにっきとかそのへんで盛り上がってましたが。

関数型だと目線が右から左に流れるのがとっつきにくく感じられるのかなぁとふと思った。

例:

[1,2,3].map{|i| i.to_s}.join(",")          #→
(string-join (map number->string '(1 2 3)) #←
             ",")

#だとすると、アラビア語圏の人は関数型言語に強い?

C言語でも関数適用を何段もネストすると下のような書き方になる……のだけど、 たぶん中間変数を置いて何行かに分けて書く人の方が多いと思う。

[prog] よろしい、ならば逆ポーランドLispだ

(('(1 2 3) number->string map) "," string-join)

駄目かな^^;

というか既にありそう

本日のツッコミ(全1件) [ツッコミを入れる]

色々ありますが、 [v-language とかでしょうか。 http://code.google.com/p/v-language/]


2007-02-14

[haskell][ruby] 最も徳の高いfactorial

via http://d.hatena.ne.jp/omochist/20061219/1166544123

コメント欄より:

factorial n = product [1..n]

Haskell格好良すぎてずるい。

Rubyならここはinjectですよね。

def factorial(n)
  (1..n).inject{|a,b| a*b}
end

2007-02-15

[ruby] 海外で入れてるgemを曝すのが流行ってるらしいらしいですよ

ということでやってみた。

yhara@meteor:~/src/ruby/gem_survey % gem list|grep '^[a-zA-Z]'
activesupport (1.4.0)
hoe (1.1.7, 1.1.6, 1.1.3)
hpricot (0.5, 0.4.92, 0.4)
mechanize (0.6.4, 0.6.3, 0.3.1)
narf (0.7.3)
newgem (0.7.2)
rake (0.7.1)
redgreen (1.2)
rspec (0.7.5.1, 0.7.5, 0.7.4, 0.2.0)
rubyforge (0.4.0, 0.3.2, 0.3.1)
scrapi (1.2.0)
sources (0.0.1)
tidy (1.1.2)
wirble (0.1.2)
ZenTest (3.4.3, 3.4.2)

すくねえw

まぁgemで入れたライブラリでちゃんと使ってるのってrspec, hpricot, mechanizeくらいだもんなぁ。

海外で晒されているリストには聞いたことないgemも一杯あってどれがどういうgemなのか知りたくなったけど、 一個ずつ調べるのも面倒なのでスクリプトを書いてみた。

(1)gemリストをtxtに保存

yhara@meteor:~/src/ruby/gem_survey % cat bryan.txt
actionmailer (1.3.2, 1.3.1, 1.2.5)
actionpack (1.13.2, 1.13.1, 1.12.5)
...(省略)

(2)こんなスクリプト

yhara@meteor:~/src/ruby/gem_survey % cat survey.rb
ARGF.each do |line|
  name = line.split.first
  puts `gem query -r -n "\\A#{name}\\z"`.split(/\n/).slice(3..-1) || "(gem #{name} not found)"
  $stdout.flush
end

(3)実行すると各gemの1行解説が読めます

yhara@meteor:~/src/ruby/gem_survey % ruby survey.rb < bryan.txt| tee tmp.txt
actionmailer (1.3.2, 1.3.1, 1.3.0, 1.2.5, 1.2.4, 1.2.3, 1.2.2, 1.2.1, 1.2.0, 1.1.5, 1.1.4, 1.1.3, 1.1.2, 1.1.1, 1.0.1, 1.0.0, 0.9.1, 0.9.0, 0.8.1, 0.8.0, 0.7.1, 0.7.0, 0.6.1, 0.6.0, 0.5.0, 0.4.0, 0.3.0)
    Service layer for easy email delivery and testing.
actionpack (1.13.2, 1.13.1, 1.13.0, 1.12.5, 1.12.4, 1.12.3, 1.12.2, 1.12.1, 1.12.0, 1.11.2, 1.11.1, 1.11.0, 1.10.2, 1.10.1, 1.9.1, 1.9.0, 1.8.1, 1.8.0, 1.7.0, 1.6.0, 1.5.1, 1.5.0, 1.4.0, 1.3.1, 1.3.0, 1.2.0, 1.1.0, 1.0.1, 1.0.0, 0.9.5, 0.9.0, 0.8.5, 0.8.0, 0.7.9, 0.7.8, 0.7.7, 0.7.6, 0.7.5)
    Web-flow and rendering framework putting the VC in MVC.
...(省略)

[Plagger] Publish::PipeがYAMLを吐けるようにしてみた

PRaggerに対して「それCustomFeed::Script/Filter::Pipe/Publish::Pipeでよくね?」と思ってたんだが、 実はPublish::PipeだとタイトルとURLしか扱えないことに気付いた。

というわけで、Publish::PipeがYAMLを吐けるようにするパッチ。

5a6
> use Data::Serializer;
20,22c21,29
<     for my $entry ($args->{feed}->entries) {
<       print $out $self->convert($entry->title) . "\n";
<       print $out $self->convert($entry->permalink) . "\n\n";
---
>     if ($self->conf->{yaml}) {
>         my $serializer = Data::Serializer->new(serializer => 'YAML');
>         print $out $serializer->raw_serialize(Plagger::Walker->serialize($args->{feed}));
>     }
>     else {
>         for my $entry ($args->{feed}->entries) {
>             print $out $self->convert($entry->title) . "\n";
>             print $out $self->convert($entry->permalink) . "\n\n";
>         }

config.yamlに

  - module: Publish::Pipe
    config:
      command: ruby sample_publish_pipe.rb
      yaml: 1

みたいに書いておくと(yaml: 1 が重要)、

---
author: yhara
description: ''
language: ~
link: http://mono.kmc.gr.jp/~yhara/d/
meta: {}
source_xml: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>(略)"
tags: []
title: Greenbear Diary
type: feed
url: http://mono.kmc.gr.jp/~yhara/d/index.rdf
entries:
  -
    author: yhara
    body: "<h3>(中略)ツッコミを入れる</a></p>"
    date: 2007-02-15T06:26:38+09:00
    enclosures: []
    feed_link: http://mono.kmc.gr.jp/~yhara/d/
    id: http://mono.kmc.gr.jp/~yhara/d/?date=20070215#p01
    link: http://mono.kmc.gr.jp/~yhara/d/?date=20070215#p01
    meta: {}
    rate: 0
    summary: "ということでやってみた。(略)"
    tags:
      - ruby
    title: "海外で入れてるgemを曝すのが流行ってるらしいらしいですよ"
    widgets: []
  -
    author: yhara
    ...

みたいなYAMLがRubyスクリプトに入力されます。あとは require 'yaml'; data = YAML.load(ARGF.read) とかしてお好きにどうぞ。

もちろんYAMLが読める言語ならPythonでもJavaでもPHPでもなんでもOKです。



2007-02-17

[ruby][event] Ruby勉強会@関西-14

行ってきました。昼70人夜50人とか凄いよなぁ。ほんと。

Ruby 実行環境を取り巻く世界

#衝撃検知機能を実装したり、スライドいじったりしてて聞いてる余裕がありませんでした^^; ごめんなさい。

Plagger meets Ruby

自分の。Plaggerネタもちょっと時期を逃したかなぁ…と思ってるうちにPrhaggerとかPRaggerとかYahoo! Pipesとか 盛り上がってきてわーい。という。

以下自分用メモ

受けたネタとそうでないもの
  • 「あまりよく知らない」の層が思ったより多かった(半分くらい?)。
  • ていうかRuby関西の異様なまでの客層の広さを忘れてた
  • ○○やさんは小ウケ(客層的にまぁそうだわな…)
  • 振動検知のアレは非常に受けが良かった(拍手された…!) 直前のプレゼン中に頑張って実装したんだけど(西本さんごめんなさい^^;)、頑張って良かった。
  • Tateyomiはまあまあ。
  • ポーランドもまあまあ。
  • Rの発音はわりとw
Keep
  • スライドの「余り」を用意するのは有用。(両方使えるとは思ってなかったけど… :-) 運が良かった。かずひこさんに感謝)
  • 1枚あたりの文字密度が低い*1ほうが話しやすい。量は増えるけど。頑張ってスライド作る。
  • ブラウザ開いて見せるより、スクリーンショットをpptに貼っておくべき(楽だし手順で混乱しなくて済む)。
  • デモはちゃんと動いた。何回も試験しといて良かった(直前になってバグに気付いたりとか^^;)
Problem
  • 準備遅すぎすぎ(略)すぎすぎる。猛省。ていうか前日になってから作り始めるとか意味不明(結局一睡もできず)
  • 徹夜すればなんとかなるかも…とか思った時点で負け
  • スクリーン見すぎだったと思う。心に余裕を持ちたい。
Try
  • 事前に1回くらい練習したい。
  • 小ネタも全部実装してデモする(もしくはスクリーンショット)。
  • (できれば)客層を考える。
  • あとWiiリモコン使いたいw

発表資料のアップロードはちょっとお待ち下さい。今週半ばには多分。

Rubyリファレンスマニュアル刷新計画の進捗報告

参加したい!

letmesee + royal-fpwで超快適フランス語辞書生活

let me seeの存在は知っていたのですが、 使ったことがなかったので(CD-ROM辞書持ってないし)手を上げるのをためらっちゃいました。上げたらよかったか。

Ruby 初級者向けレッスン 第11回

irbについて。

隣の人(大学2回生)と一緒に。なんでもゼミの課題で「複数人で本をひとつ書け」*2 というのがあるらしく、 RubyかRailsの本にしようかと考えているそうな。凄いなぁ。頑張ってください。

懇親会

  • 氏久さん曰く2/24の夜に京都でLiveCodingがあるとのこと。見に行かねば。
  • はこべさんの932は草津?と思ってたらBKC生とのこと。近ぇ!(笑)(でも厳密にいうと373932ですよね)
  • Rubyがシェアを拡大していく様子は初期のJavaを思い起こさせるらしい(こんなにJavaが普及するとは/Javaはむしろ軟派(?)な言語だった)

最後に一言

関西を離れるスタッフの方、これまでどうもお疲れ様でした m(__)m *3

*1 宮川さんのスライドを参考にしてみた

*2 出版するのかな??

*3 次の勉強会はおそらく4月以降なので、一足先に卒業シーズン


2007-02-18

[ruby] Hirameki Inspiration - 超手抜き版 はてなダイアリーライター(ruby版)を作ってみた。

ちなみにこのエントリもスクリプトで投稿してみました:-)

カコイイ!:-)

class BlogEngine
  def initialize(url, id, password)
  end
end

class HatenaDiary < BlogEngine
  def login
  end
  def post(content)
  end
end

class TDiary < BlogEngine
  def login
  end
  def post(content)
  end
end

みたいな感じで他のブログにも対応させられないかな。


2007-02-20

[Plagger][ruby] Plagger meets Ruby

この前のRuby勉強会@関西-14で発表したときの資料を公開します。

PlaggerのプラグインをRubyで書く方法について、図とか例とかを交えつつ解説する、という感じでやらせてもらいました。 30分弱。

いやー、feedalizerとかfeedtoolsとかHpricotとMechanize便利すぎという話も入れようかと 思ってたんだけど、時間的にも容量的にも余裕がなかったので残念ながらカット。

スライドのデザインは http://gift.her.jp/pp_template/ のものを使わせてもらいました。スライド作成意欲が2割くらいアップしました。 ありがとうございます。


まとめを以下に置いておきます。

RubyでPlaggerのプラグインを書こう

stdin/stdout

(1)RubyからPlaggerに入力

 - module: Subscription::Config
    config:
      feed:
        - url: "script:ruby make_feed.rb"

  - module: CustomFeed::Script

Ruby側では、フィードのXMLかYAMLを出力する

  print YAML.dump( 
    "title" => "hogehoge diary",
    "entry" => entries.map{|ent|
      { 
        "title" => ent.title,
        "author" => ent.author,
        "link" => "http://.../#{ent.id}",
        "date" => ent.date,
        "body" => ent.message.toutf8,
      }
    }) 

例:customfeed_cvslog.rb

(2)Rubyをフィルタにする

 - module: Filter::Pipe
    config:
      command: "ruby some_filter.rb"
      encoding: euc-jp

Ruby側では、HTMLを受け取ってHTMLを返す*1

例(案):Filter::RubyMan Filter::Tateyomi

(3)PlaggerからRubyへ出力

 - module: Publish::Pipe
    config:
      command: 'ruby feed_writer.rb'

Ruby側では、YAMLを受け取る(ただしパッチが必要→[Plagger] Publish::PipeがYAMLを吐けるようにしてみた)

例:publish_rubysdl.rb

*1 yamlにtext-only: 1 を付け加えれば、テキスト部分ごとに受け取ることも可能


2007-02-23

[softs] Foxit Reader

高速・軽量なPDFリーダ。

これは快適すぎる!


2007-02-24

[prog] キューとスタック

ふと気付いたんだが、エスカレーターはFIFOでエレベーターはLIFOですね。

[event] LiveCoding第3回に行ってきた

面白かった!スタッフの方、出演者の方どうもありがとうございました。

感想:

  • スイッチ楽しい
  • ピザを頼むのは案外難しい

[ruby] Webサービス経由で足し算を行うライブラリ:jitor-int.rb

LiveCoding中に氏久さんが作ったIntというサービスを利用するライブラリを作ってみました。

Intは整数の演算を行うWebサービスで、例えば http://int.jitor.net/add/3/4 にアクセスすると"7"と書かれたページが 得られます。

さて、これをRubyから使うにはどういうAPIが良いでしょうか?JitorInt.add(3,4) とか? いやいや、こんな長いのは覚えにくいし、足し算を行うのにこんなのをいちいち書いてられませんよね。 「良くつかうものは短い名前にする」という原則に従えば、ここは組込みのFixnum#+をオーバーライドするしか ありません。

というわけで、jitor-int.rbです。

require 'open-uri'

class Fixnum
  def use_orig
    alias :+ :add_orig
    result = yield
    alias :+ :add_jitor
    result
  end

  def add_jitor(other)
    url = "http://int.jitor.net/add/#{self}/#{other}"
    puts "accessing to #{url}..."

    result = use_orig{ open(url).read }

    p result
    result.to_i
  end

  alias :add_orig :+
  alias :+ :add_jitor
end

これを使うと、例えば

require 'jitor-int'

p 1 + 2 + 3

のようなスクリプトが

accessing to http://int.jitor.net/add/1/2...
"3"
accessing to http://int.jitor.net/add/3/3...
"6"
6

のような感じで動きます。require 'jitor-int' と書くだけで、Webサービス経由で足し算できるようになります。簡単ですね :-)

このライブラリがあれば、突然Rubyで足し算ができなくなっても安心!…と言いたいところなのですが、 実はopen-uriが中で Fixnum#+ を使っているため組込みの「+」は残念ながら必要です。

また普通にオーバーライド しただけでは無限再帰になってしまうので、Webにアクセスするところだけ + の定義を組込みのものに戻しています。

おまけ

「-」「*」「%」にも対応したバージョンを以下に貼っておきます。(「/」がないのは仕様です。)

require 'open-uri'

class Fixnum
  SYMS = {
    :+ => :add,
    :- => :sub,
    :* => :multi,
    :% => :mod
  }
  def switch_method(sym, method)
    self.class.instance_eval do
      alias_method sym, method
    end
  end

  def use_orig
    SYMS.each{|sym, name| switch_method(sym, "#{sym}_orig")}
    result = yield
    SYMS.each{|sym, name| switch_method(sym, name)}
    result
  end

  def jitor(type,a,b)
    url = "http://int.jitor.net/#{type}/#{a}/#{b}"
    puts "accessing to #{url}..."

    result = use_orig{ open(url).read }

    p result
    result.to_i
  end

  SYMS.each do |sym, name|
    define_method name do |other|
      jitor(name, self, other)
    end
    alias_method "#{sym}_orig", sym
    alias_method sym, name
  end

end

2007-02-25

[misc] 3/1まで出かけてます

T/O

[Pragger] PRaggerだってピザを頼みたい

ですよね!

というわけで notify_demaecan.rb というプラグインを書いてみました。

Notify::Pizzaより高機能な点として、ピザのサイズが選べることが挙げられます(笑)。 例えばconfigに「size: L」のように書いておくと、Lサイズのピザの中からランダムで一つが選ばれます。 これでパーティーの際にも安心。

注意:

  • テストしてません(ピザを頼む用事がなかったので)。 多分いけるとは思う。
  • エラー処理が適当です。
  • まだ寿司には対応してません。(対応は簡単なんだけど、失敗すると\8,000-とかかかるので危ないw)
  • townという引数がありますが実装されてません。会員登録のときに住所も登録しておいてください
  • 文字コードについて真面目に考えてません。出前館のWebページはSJISです

というわけで、どなたか実際に試していただけると有難いです^^;

require 'rubygems'
require 'mechanize'

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

  def order(type="ピザ", size="L", town="0")
    #start
    page = @agent.get("http://demae-can.com/index.php?action=dream_login_index")

    #login
    form = page.forms.first
    form["id"] = @user
    form["pass"] = @pass
    page = form.submit

    unless page.root.to_html.include? "http://demae-can.com/search/shop_list.html\?word=0"
      raise ArgumentError, "login failed."
    end

    #select type
    page = @agent.get("http://demae-can.com/search/shop_list.html\?word=#{town}")
    link = page.links.find{|link| link.node.to_html.include?(type)}
    page = link.click

    #select shop
    link = page.links.find{|link| link.href =~ /shopcode/}
    page = link.click

    #select size
    forms = page.forms.find_all{|form|
      (form.form_node/"td.mini").find{|td| td.inner_text =~ /#{size}サイズ/}
    }
    page = forms[rand(forms.size)].submit

    #select number
    page = page.forms.first.submit

    #select time
    page = page.forms.first.submit

    #submit!!
    form = page.forms.first
    form["pass"] = @pass
    page = form.submit  
  end
end

def notify_demaecan(config, data)
  demae = DemaeCan.new(config["user"], config["password"])
  demae.order(config["type"], config["size"])
end

=begin
 - module: notify_demaecan
   config:
     user: AABCDEF
     password: pAssw0rD
     type: ピザ
     size: L
=end

#将来的にはgem化して

gem install demaecan

とかできると格好いい…かも。

本日のツッコミ(全3件) [ツッコミを入れる]

中本 [次の CodeFest のときにでも是非使ってください!]

yhara [おお、そんな手が…あるのか!?]

中本 [だって、いつもg新部さんが電話で注文してるし...]