63
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

HappyMapperっていうXMLパーサーを見つけてね,いい感じだから紹介したかったんだけどそもそも…

Last updated at Posted at 2014-01-10

RubyでXMLのパースってどうしてんの?

Rubyに限らないけどXML/HTMLのパースって結構する頻度高いよね
で,いつもNokogiri使ってたんだけど特定のタグ以下のデータをすべて使うとかよくある場面なのだけどいちいち指定しないといけないのちょっとめんどくさい
せっかく木構造なんだからオブジェクトに直接マッピングすればいいのにね!
って思って調べてみると何やらHappyMapperとかいう素敵なライブラリがあるらしい
なのに日本語の記事とか一切見なくていっぱい悲しかったので書くことにした

調べてみた

とりあえずちょっとしたコード書いて紹介していくのぜ
ニコニコ見ながらこの記事作ってるのでニコニコのRSSをそれぞれでパースしてみるよ
例のアレの総合ランキング(毎時)-ニコニコ動画
うn requre 'rss' とかいわない
とりあえず /rss/channel/item/title を全部(100個)引っ張ってみた
わかりやすいように丁寧にパースしてるよ

環境は

  • ruby 1.9.3p194
  • gem 1.8.23

ですん
インストール等はググるなり下記のページから飛ぶなりすればいいと思うよ

コードだよ

RSSのデータを取ってくる
よくやるいつものこのコード

require 'net/http'
require 'uri'

uri = URI.parse "http://www.nicovideo.jp/ranking/fav/hourly/are?rss=2.0"
res = Net::HTTP.get uri

XMLを文字列でとってきたのでこれをそれぞれのパーサーに投げてみる

REXML

標準ライブラリ
これだけでも十分便利

require 'rexml/document'

doc = REXML::Document.new res
titles_rx = []
doc.elements.each('/rss/channel/item/title'){|e| titles_rx << e.text}
titles_rx.first(5).each{|e| puts e}

基本はこんな感じ

Nokogiri

みんな大好き!高性能なXMLパーサー
一番の特徴はcssのセレクタが使えるところですかね
パーサーでは一番使われていると思う(当社比)
XMLよりHTMLのほうが真価を発揮できるんだけど,今回はXMLでしかも単純なものなのでほとんど力を発揮できてなくてかわいそうだけどホントは出来る子なんです

require 'nokogiri'

xml = Nokogiri::XML res
titles_xpath = xml.xpath("/rss/channel/item/title").map{|e| e.content}
titles_xpath.first(5).each{|e| puts e}

REXMLとほとんど変わらないけどmapでちょっといい感じになる
ステキステキー

HappyMapper

この記事の主役ですね.
上記のような単なるパース機能だけじゃなくオブジェクトへのマッピングもできちゃうスグレモノ
O/RマッパーならぬObject/XMLマッパーとでも言うべき?

require 'happymapper'

module Nico
  class Item
    include HappyMapper
    
    element :title, String
  end
  
  class Channel
    include HappyMapper
  
    has_many :item, Item
  end

  class Rss
    include HappyMapper
    
    has_one :channel, Channel
  end
end

titles_hm = Nico::Rss.parse(res).channel.item.map{|e| e.title}
titles_hm.first(5).each {|e| puts e}

コード長くなってるじゃねぇか!
って思うかもしれないけど精神衛生的にだいぶよろしいと思いませんか(重症)
railsユーザーも見覚えある感じだと思う
それに実際に取り出してる所を見てみると

titles_hm = Nico::Rss.parse(res).channel.item.map{|e| e.title}

う・・・うつくしい・・・はっ!?
と,とにかく!文字列による操作ではなくメソッドチェーンで掘り下げているとてもRubyらしいコードで僕も満足です(ほっこり)
うま味が少ないと思うかもしれませんが実際その通りなのでぐうの音も出ないけど,それはこんな単純な操作なのがいけないんだ!俺は悪くねぇ!
ってことでもうちょっと踏み込んでみるよ

で,めんどくさい感じのところはどうなんよ?

それぞれライブラリがのどういうものかなんとなく感じていただけたと思う・・・(思わない)
そんなこんなで冒頭に言った「特定タグ以下のデータを取ってくるのにどれだけめんどくさくなるか」を比較してみたよ
itemタグ以下のtitle,link,pubDate,descriptionのデータをとってみる

class Item
  attr_accessor :title, :link, :pubDate, :description
end

これに入れるよ
めんどくさいので今度は手抜きコードだよ

REXML

items_rx = []
doc = REXML::Document.new res
doc.elements.each('//item') do |e|
  i = Item.new
  i.title = e.elements['title'].text
  i.link = e.elements['link'].text
  i.pubDate = e.elements['pubDate'].text
  i.description = e.elements['description'].text
  items_rx << i
end
items_rx.first(5).each{|e| puts e.title}

DRYな人にはつらいコード

Nokogiri

xml = Nokogiri::XML res
items_css = xml.css("item").map do |e|
  i = Item.new
  i.title = e.css("title").first.content
  i.link = e.css("link").first.content
  i.pubDate = e.css("pubDate").first.content
  i.description = e.css("description").first.content
  i
end
items_css.first(5).each{|e| puts e.title}

Nokogiriの方がつらみがある
.first挟まないといけなかったり最後にiを評価してるところには哀愁すら感じる

HappyMapper

class Item
  include HappyMapper
  tag "item"
  element :title, String
  element :link, String
  element :pubDate, DateTime
  element :description, String
end

items_hm = Item.parse(res)
items_hm.first(5).each {|e| puts e.title}
puts "------\n"

wow
such extreme
parseメソッド呼ぶだけでだけでオブジェクトにすーっとマッピングしてくれる・・・これはありがたい
行数もそんなに変わらないし増えてくるとこっちのほうがいいよね!
これなら納得いただけるだろう!ふんす!(満足気)

疲れてきたのでそろそろまとめるね・・・

特徴まとめる

  • REXML
    • 標準ライブラリなのでインストール不要
    • xpathで指定
    • 機能は他に比べると弱い(十分だけど)
  • Nokogiri
    • gem install nokogiri
      • libxmlとかいる
    • cssとxpathで指定
    • 痒いところに手が届く
  • HappyMapper
    • gem install happymapper
    • オブジェクトのデータ階層で指定
    • お膳立てがいるけど便利

という感じ?詳しくはググれ

つまり

環境汚したくない時や特定のタグだけ取れればいいって時はREXML
HTMLをパースしたい時や初心者でよくわからない人は全部のせのNokogiri
取得したいタグが多い時やオブジェクトにマッピングしたい時はHappyMapper

こんなところですかね

おまけ

REXML

他にもREXMLにはSAXと呼ばれるスタイルで書くためのライブラリもある(今回のはDOMスタイルっていう奴)
なんかリスナーを登録するとなんかやってくれるらしいっすよ(他人事)

  • rexml/parsers/sax2parser
  • rexml/parsers/streamparser

さらに他にも

  • rexml/parsers/pullparser
  • rexml/parsers/ultralightparser

でもよくわからないので今回は紹介しなかったよ!
だってややこしいんだもん!もん!

他にも

ActiveSupportのcore_extを導入するとHashのクラスメソッドにfrom_xmlってのが追加されるみたいで
これはXMLをHashで返してくれるメソッドみたい
使った事無いので知らない

require 'active_support/core_ext/hash/conversions'
hash = Hash.from_xml(xml)

きっとこんな感じで利用できて,あとは

hash["rss"]["channel"]["item"].map{|e|e["title"]}

みたいな感じでとれるんだと思う(てきとー)
一発でパースするのは便利そう
だけどHashは微妙に扱いづらいので敬遠しちゃうかも

おわりに

nokogiri-happymapperっていうのがあってNokogiriとHappyMapperのいいとこ取りした奴がある(早く言えよ)
マッピングしたいデータをcssで指定してちぇけだーん!ワザマエ!
困ったらこれ入れてパパパッとやって終わり!

オチがついたところで以上!
今度はHappyMapperの解説ができるといいですね(するとはいってない)

yamlとかjsonとかも同じ感じで出来ればもっと捗ると思うんで誰かお願いします
って誰かが言ってました(言いだしっぺ回避)

参考

63
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?