REXMLにもパーサがいろいろあるので、各パーサの速度を計ってみた。
※各パーサの使い方はこちらのページを参考にした。
結果
PullParserが結構速い。
逆に、SAX2Parserが意外に遅い。使い方間違ったかしら…
~$ ruby -r profile bench_parser.rb 2>&1 | egrep "( \% | time |parse_by_)"
% cumulative self self total
time seconds seconds calls ms/call ms/call name
0.11 58.17 0.06 1 64.00 9969.00 Object#parse_by_pullparser
0.00 60.03 0.00 1 0.00 7265.00 Object#parse_by_streamparser
0.00 60.03 0.00 1 0.00 22500.00 Object#parse_by_domparser
0.00 60.03 0.00 1 0.00 19766.00 Object#parse_by_sax2parser
結論
∧_∧ カタカタ / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ( ) ∧ ∧ < PullParserはオススメですよ…と。 ( ) (,,゚Д゚) \____________  ̄ ̄ ̄ ̄ ̄ (つ_つ____  ̄ ̄ ̄日∇ ̄\|ThinkPad|\  ̄ ========= \
まあ、適当な計測なんであんまり信用はできないけど…
スクリプト
はてブの人気エントリのRSSを取ってきて、タイトルとURLのalistを作成してるだけ。
キャッシングされるかも知れないので、RSSは一度ファイルに落として、いちいち開きなおす。
require 'open-uri' require 'rexml/document' require 'rexml/parsers/baseparser' require 'rexml/parsers/pullparser' require 'rexml/parsers/sax2parser' require 'rexml/parsers/streamparser' require 'rexml/sax2listener' require 'rexml/streamlistener' class SAX2ParserListener include REXML::SAX2Listener def initialize @titles = [] @links = [] end def start_element(uri, localname, qname, attributes) @in_item_tag = true if localname == 'item' end def end_element(uri, localname, qname) return unless @in_item_tag case localname when 'title' @titles << @text when 'link' @links << @text when 'item' @in_item_tag = false end end def characters(text) @text = text end def items @titles.zip(@links) end end def parse_by_sax2parser(f) parser = REXML::Parsers::SAX2Parser.new(f) listener = SAX2ParserListener.new parser.listen(listener) parser.parse listener.items end class StreamParserListener include REXML::StreamListener def initialize @titles = [] @links = [] end def tag_start(name, attrs); @in_item_tag = true if name == 'item' end def tag_end(name) return unless @in_item_tag case name when 'title' @titles << @text when 'link' @links << @text when 'item' @in_item_tag = false end end def text(text) @text = text end def items @titles.zip(@links) end end def parse_by_streamparser(f) listener = StreamParserListener.new REXML::Parsers::StreamParser.new(f, listener).parse listener.items end def parse_by_pullparser(f) parser = REXML::Parsers::PullParser.new(f) parse_item = lambda {|event| title = link = nil while (event = parser.pull) and not (event.end_element? and event[0] == 'item') if event.start_element? and event[0] == 'title' title = parser.pull[0] elsif event.start_element? and event[0] == 'link' link = parser.pull[0] end end [title, link] } rs = [] while parser.has_next? event = parser.pull next unless event.start_element? next unless event[0] == 'item' rs << parse_item.call(event) end rs end def parse_by_domparser(f) root = REXML::Document.new(f).root root.get_elements('item').map {|item| [item.get_text('title').value, item.get_text('link').value] } end def log(name, items) sample = items.first puts <<EOS --- #{name}: size: #{items.size} sample: title: #{sample[0]} link: #{sample[1]} EOS end open('http://b.hatena.ne.jp/hotentry?mode=rss') {|f| open('sample.xml', 'w') {|out| out << f.read } } open('sample.xml') {|f| items = parse_by_domparser(f) log('domparser', items) } open('sample.xml') {|f| items = parse_by_pullparser(f) log('pullparser', items) } open('sample.xml') {|f| items = parse_by_streamparser(f) log('streamparser', items) } open('sample.xml') {|f| items = parse_by_sax2parser(f) log('sax2parser', items) }