MAGAZINE
ルーターマガジン
Ruby
Nokogiriのat_cssメソッドで取得した要素(ノード)を完全に複製する方法
2019.11.08
Nokogiriのdupメソッドでは、親兄弟は複製できないという話
NokogiriのNodeクラスに定義されているdupメソッドは、ノード自身とその子孫のみ複製します
すなわち、Nokogiriのdupメソッドでは、親要素・兄弟要素は複製出来ないのです。
いやでも、親兄弟も欲しいんだけど・・・と、僕は思ったので、実際に親兄弟も複製した方法を紹介します。
忙しい人向け
親要素や兄弟要素も複製したければDocumentクラスから複製する。
require 'nokogiri'
html = <<'EOS'
<html>
<body>
<h1>My First Heading</h1>
<div>
<p>My first paragraph.</p>
</div>
</body>
</html>
EOS
def complete_nokogiri_dup(node)
doc_dup = node.document.dup
doc_dup.at_css(node.css_path)
end
doc = Nokogiri::HTML.parse(html)
div_node = doc.at_css('div')
p complete_nokogiri_dup(div_node).parent # => <Nokogiri::XML::Element:0x3fde220e43f0 name="body" children=[#, #
Nokogiriのdupメソッドの各要素に対する挙動
・子要素
p div_node.at_css('p') # => <Nokogiri::XML::Element:0x3fc163838934 name="p" children=[#]>
p div_node.dup.at_css('p') # => <Nokogiri::XML::Element:0x3fc163838934 name="p" children=[#]>
子要素はちゃんと複製できているみたいですね。 それでは、兄要素はどうでしょうか。
・兄要素
p div_node.previous_element # => <Nokogiri::XML::Element:0x3fcc048287cc name="h1" children=[#]>
p div_node.dup.previous_sibling # => nil
previous_siblingは兄要素、つまり一つ前の要素であるh1タグを参照するはずのメソッドです。複製したdiv_nodeに実行するとnilが返されますので、兄要素は複製できていないようですね。続いて親要素も見てみましょう。
・親要素
p div_node.parent # => <Nokogiri::XML::Element:0x3fd6891023c8 name="body" children=[#, # nil
p div_node.dup.parent # => nil
兄要素と同様にnilが返されます。親要素も複製できていないみたいです。
完全複製のやり方
def complete_nokogiri_dup(node)
doc_dup = node.document.dup
doc.at_css(node.css_path)
end
doc = Nokogiri::HTML.parse(html)
div_node = doc.at_css('div')
p complete_nokogiri_dup(div_node).parent # => <Nokogiri::XML::Element:0x3fde220e43f0 name="body"
dupメソッドで子要素は複製出来るという点を活かして、全てのノードの最上位にあるDocumentクラスから複製します。この複製したDocumentに対して、複製元ノードと同一のCSSセレクタを当てることで、親子兄弟関係を保ったままのノードの複製を作成しています。
終わりに
最後まで目を通していただきありがとうございます。弊社は、Webクローリングやスクレイピングのナレッジが沢山溜まっています。rubyを主としてスクレイピングは開発しているため、特にNokogiriのこういった小ネタは沢山あります。気になる方は、他の方のブログも覗いてみてください。
CONTACT
お問い合わせ・ご依頼はこちらから