MAGAZINE

ルーターマガジン

クローリング/スクレイピング

NokogiriでCSSセレクタとノードの深さを取得する方法

2018.10.24
Pocket

こんにちは。学生エンジニアのKawaguchiです。
今回は、Nokogiriを用いてCSSセレクタを取得するメソッドと、ノードの深さを取得する方法を紹介します。

Nokogiriについてはこちらの記事を参考にしてください。

css_pathメソッドについて

Nokogiriにはcss_pathという標準メソッドがあり、
このメソッドはノードのhtmlタグまでのCSSセレクタ (例: html > body > div > div:nth-of-type(1) > p:nth-of-type(1)) を返してくれます。(classとidは取得出来ません。)
そして、CSSセレクタの「>」を数えるという単純な方法ですが、ノードの深さを取得することが出来ます。

ノードの深さを取得するサンプルコード

今回はこちらのsample.htmlというHTMLを対象にしていきます。

<html>
  <body>
    <div id="main">
      <h1>商品一覧</h1>
      <div>
        <p class="name">商品名1</p>
        <p class="price">価格1</p>
      </div>
      <div>
        <p class="name">商品名2</p>
        <p class="price">価格2</p>
      </div>
    </div>
  </body>
</html>
require 'nokogiri'

html = open('sample.html').read
doc = Nokogiri::HTML.parse(html)

doc.css('p').each do |node|
  puts node.text
  puts node.css_path
  puts node.css_path.count('>') + 1
  puts ''
end

このように、CSSセレクタでpタグを指定し、ノードのテキスト、CSSセレクタ、ノードの深さを出力させると、以下のような結果が得られます。

商品名1
html > body > div > div:nth-of-type(1) > p:nth-of-type(1)
5

価格1
html > body > div > div:nth-of-type(1) > p:nth-of-type(2)
5

商品名2
html > body > div > div:nth-of-type(2) > p:nth-of-type(1)
5

価格2
html > body > div > div:nth-of-type(2) > p:nth-of-type(2)
5

ノードの深さは無事に取得出来るようになったのですが、
classとid付きのCSSセレクタを別途利用することになり、css_pathメソッドでは、
classやidを取得することができないので、他にいいメソッドがないか調べてみました。
しかし、見つからなかったので、classとidも取得可能なメソッドを作成しました。

classとid付きのCSSセレクタを取得するメソッドについて

作成したのはcss_path_with_class_idというメソッドで、簡単に説明すると、
引数のnodeからcss_pathメソッドで得られるCSSセレクタを「 > 」で区切った配列にし、
それぞれのタグの後に、classは「.」、idは「#」を前に付加し、追加させています。

require 'nokogiri'

def css_path_with_class_id(node)
  tag_with_class_id_array = []

  node.css_path.split(' > ').reverse.each do |tag|
    tag += ".#{node['class']}" if node['class']
    tag += "\##{node['id']}" if node['id']
    tag_with_class_id_array << tag
    node = node.parent
  end

  tag_with_class_id_array.reverse.join(' > ')
end


html = open('sample.html').read
doc = Nokogiri::HTML.parse(html)

doc.css('p').each do |node|
  puts node.text
  puts node.css_path
  puts css_path_with_class_id(node)
  puts ''
end

先ほどのように、CSSセレクタでpタグを指定し、出力させると、

商品名1
html > body > div > div:nth-of-type(1) > p:nth-of-type(1)
html > body > div#main > div:nth-of-type(1) > p:nth-of-type(1).name

価格1
html > body > div > div:nth-of-type(1) > p:nth-of-type(2)
html > body > div#main > div:nth-of-type(1) > p:nth-of-type(2).price

商品名2
html > body > div > div:nth-of-type(2) > p:nth-of-type(1)
html > body > div#main > div:nth-of-type(2) > p:nth-of-type(1).name

価格2
html > body > div > div:nth-of-type(2) > p:nth-of-type(2)
html > body > div#main > div:nth-of-type(2) > p:nth-of-type(2).price

classとid付きのCSSセレクタを得られるようになりました。

さいごに

こうしたDOM構造の特徴解析など、様々な自前技術を盛り込んだのがルーターイチオシのAIクローラーです!これまでの定型的な情報収集だけのクローラーから1歩も2歩も進化し、例えば「会社の役職がある人の情報」という指定で自動的にインターネットの公開情報の中から情報を収集したりできるようになります。

弊社エンジニアは今後も「一つ先を行くクローラー」の開発を行っていきます!クローリング/スクレイピングのお悩みがあればぜひお問い合わせ下さい。

Pocket

CONTACT

お問い合わせ・ご依頼はこちらから