末尾に「/page=n/」(n はページ番号)がつくような URI を生成したい場面の話です。
Enumerator::Lazy で URI生成
URI生成の簡単な例です。
sample1.rb
mk_uri_gen = -> n = 0 { -> { "http://hoge/hoge/page=#{n += 1}/" } }
ug = mk_uri_gen.()
p ug.() #=> "http://hoge/hoge/page=1/"
p ug.() #=> "http://hoge/hoge/page=2/"
p ug.() #=> "http://hoge/hoge/page=3/"
:
これは、Enumerator::Lazy の仕組みを使い以下のようにできます。
sample2.rb
ug = (1..Float::INFINITY).lazy.map{|n| "http://hoge/hoge/page=#{n}/" }
p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:
Enumerator::Lazy を使う場合は rewind や take, each などが使えて便利です。
:
p ug.next #=> "http://hoge/hoge/page=4/"
p ug.next #=> "http://hoge/hoge/page=5/"
ug.rewind # 巻き戻し
p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
:
# 最初の5つ分の繰り返し
ug.take(5).each do |uri|
p uri
end
# 無限の繰り返し (注)ずっと処理が続きます
ug.each do |uri|
p uri
gets # ここで、キー押下待ちになります。処理をやめるときは Ctrl-C を押しましょう
end
URI テンプレートのパラメタ化
URI のテンプレートをパラメタとして渡せるようにしたいとします。
パーセント記法による書式文字列を渡す方法が考えられます。
sample3.rb
hoge = 'http://hoge/hoge/page=%{n}'
mk_uri_gen = -> tmpl { (1..Float::INFINITY).lazy.map {|n| tmpl % {n:n} } }
ug = mk_uri_gen.(hoge)
p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:
固定文字列をパラメタにしたい場合は上のようになりますが、ラムダを渡す方がより柔軟に処理できます。
sample4.rb
# 1ページ目以前には「/page=n/」をつけない URI を生成するラムダ
hoge = -> n { %(http://hoge/hoge/#{"page=#{n}/" if n > 1}) }
mk_uri_gen = -> tmpl { (1..Float::INFINITY).lazy.map(&tmpl) }
ug = mk_uri_gen.(hoge)
p ug.next #=> "http://hoge/hoge/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:
Nokogiri::HTML::Document を返すジェネレータの例
sample5.rb
require 'open-uri'
require 'nokogiri'
hoge = -> n { %(http://hoge/hoge/#{"page=#{n}/" if n > 1}) }
conv = -> uri { Nokogiri::HTML open uri }
mk_doc_gen = -> tmpl { (1..Float::INFINITY).lazy.map(&tmpl).map(&conv) }
dg = mk_doc_gen.(hoge)
# dg は Document を返すジェネレータ
dg.each do |doc| # doc は URIが指定するページを読み込んだ Nokogiri::HTML::Document オブジェクト
puts (doc/:title).map(&:text) * ' '
gets # キー押下待ち
end
おわりに
本稿内容の動作確認は以下の環境で行っています。
$ ruby -v
ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
$ gem list nokogiri
*** LOCAL GEMS ***
nokogiri (1.6.5)
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.1 LTS
Release: 14.04
Codename: trusty
参考