こんにちは、エンジニアのitogaです。

昨今の新型コロナウィルス感染症拡大の影響により、リモート研修の実施を余儀なくされ平時より研修のパフォーマンスが低下している企業さんも多々あるようです。 今回は、Nokogiriのattrメソッドで期待した値が返らない時のトラブルシューティングと、rooterの研修の中で新人さんが疑問に思うようなattrメソッドの挙動について整理していきます。

サンプルとして以下のようなhtmlを用意します。

<html>
<body>
<p title="this is title." onclick="myOnclick"/>
<p title="this is title2."/>
</body>
</html>

attrメソッドの返り値がなんか違う!!という時のトラブルシューティング

そもそも見ているhtmlは正しいのか、利用しているCSSセレクタXPATHが正しいのか、は確認してください。
以下はそれらが確認できた人のためのトラブルシューティングです。

1. attrメソッドのレシーバーのクラスを確認する

レシーバーというのは以下のようにメソッドを呼んだ時の、nodeに当たる部分になります。

node.attr('title')

これのクラスを確認します。

p node.class # ー> Nokogiri::XML::Element

2.1 レシーバーのクラスがNokogiri::XML::Elementであった場合

Nokogiri::XML::Elementでは属性値を取り出すメソッドは、以下の3つが用意されています。どれを使っても結果は同じです。

p node.attr('title') # ー> "this is title."
p node.get_attribute('title') # ー> "this is title."
p node['title'] # ー> "this is title."

2.2 レシーバーのクラスがNokogiri::XML::NodeSetであった場合

Nokogiri::XML::NodeSetは、複数のノードを扱うためのArrayのようなクラスです。複数のノードを扱うという前提があるため、1つのノードの特定の属性の値を参照するという目的に対しては適切ではありません。以下のように1つの要素を指定してNokogiri::XML::Elementを用意してから上記のメソッドを使いましょう

node = nodeset[0] # ー> 0番目のノードのNokogiri::XML::Elementを変数nodeに格納

おまけ: なぜ属性値の取得で詰まるのか

nokogiriにはattrの名を冠する紛らわしいメソッドがたくさんあるからです。この辺りを整理した情報があまり見つからなかったので以下にまとめておきます。

冒頭のサンプルに対してNokogiriパースしてnodeとnodesetを用意します。

require 'nokogiri'
doc = Nokogiri::HTML.parse(html)
nodeset = doc.css('p')
p nodeset.class # ー> Nokogiri::XML::NodeSet
node = doc.at_css('p')
p node.class # ー> Nokogiri::XML::Element

紛らわしいattrの名を冠するメソッドたち

attr(属性名)

Nokogiri::XML::NodeSetではNokogiri::XML::Attrオブジェクトを、Nokogiri::XML::Elementでは属性の値を返します。

p nodeset.attr('title') # ー> #<Nokogiri::XML::Attr:0x3fd2034e1438 name="title" value="this is title.">
p node.attr('title') # ー> "this is title."

attribute(属性名)

Nokogiri::XML::NodeSetでもNokogiri::XML::ElementでもNokogiri::XML::Attrオブジェクトを返します。

p nodeset.attribute('title') # ー> #<Nokogiri::XML::Attr:0x3fd2034e1438 name="title" value="this is title.">
p node.attribute('title') # ー> #<Nokogiri::XML::Attr:0x3fd2034e1438 name="title" value="this is title.">

attributes()

Nokogiri::XML::Elementではキーに属性名、値にNokogiri::XML::Attrオブジェクトを持った、ノードが持つ属性のハッシュを返します。Nokogiri::XML::NodeSetには定義されていないメソッドです。

p node.attributes # ー> {"title"=>#<Nokogiri::XML::Attr:0x3fd2034e1438 name="title" value="this is title.">, "onclick"=>#<Nokogiri::XML::Attr:0x3fd2034e0894 name="onclick" value="myOnclick">}
p nodeset.attributes # ー> undefined method `attributes' for #<Nokogiri::XML::NodeSet:0x00007ffa68006aa8> (NoMethodError)

おわりに

nokogiriでの属性の取得は、複数の導線が用意されておりどれを利用するべきかの判断が難しい部分でもあります。属性値を参照するだけであれば、get_attributeメソッドが他クラスのメソッドとメソッド名が競合していないので一番良いと思います。(短い書き方に惹かれて[]や.attrを利用してしまいがちですが。)

属性取得に利用するメソッドに迷った際は、ぜひ本記事をご参考にいただければと思います。

Pocket