学生エンジニアのMoriです。
今回はWebクローリングでも多用する Nokogiri を使ったスクレイピングについて書きたいと思います。

目次

1. Nokogiriとは

DOM操作するためのRubyのGemです。
Rubyでスクレイピングをする際には欠かせないGemの一つと言っても過言ではないでしょう。
これを用いてHTMLから任意の箇所の文字列を抜き出したり、動的にHTMLを作成することができます。

2. Nokogiriを使う準備

Rubyスクリプトの場合はコマンドラインから以下のコマンドを実行して、インストールします

gem install nokogiri

Railsのプロジェクトで使う際にはGemfileに以下のような記述を追加します

gem 'nokogiri'

次のコマンドをターミナルから入力し、追加したGemをインストールします

$ bundle install

3. 基本的な使い方

今回は以下のようなHTMLから情報を取得することを行いたいと思います。

<!DOCTYPE html>
<html>
  <head><title>タイトル</title></head>
  <body>
    <div>
      <div id='txts'>
        <p>1つめ</p>
        <p>2つめ</p>
      </div>
      <div id='links'>
        <a href='https://www.rooter.co.jp'>ルーターのホームページ</a>
      </div>
    </div>
  </body>
</html>

3.1 HTMLをDOMに変換する

#gemの読み込み
require 'nokogiri'

#今回はHTMLをファイルから持ってくる
html = File.open('sample.html'){|f| f.read }

#HTMLをDOMに変換
doc = Nokogiri::HTML.parse(html, 'https://www.exaple.com/index.html', 'utf-8', XML::ParseOptions::DEFAULT_HTML)
#これでもOK
doc = Nokogiri::HTML.parse(html)

サンプルでは、HTMLを引数に渡し、Nokogiriオブジェクトを生成しています。
Nokogiriオブジェクトを生成するメソッドでは、

  • 第1引数 : 解析したいHTML
  • 第2引数 : そのHTMLのURL
  • 第3引数 : 文字コード
  • 第4引数 : パースする際のオプション番号

となっています。第一引数(HTML)以外の第2引数・第3引数・第4引数は省略が可能です。

3.2 要素ノードを取得する

#要素ノードの配列が返る
doc.xpath('/html/body/div/div/p')
doc.css('p')
doc.search('p')

要素ノードの指定法として、XPathを使用する方法とCSSを利用する方法があります。
CSSは簡潔に要素ノードが指定できるため、今回はCSSを使っていきます。
(「CSSセレクタ」でググるとたくさん記事が出て来ます!)

doc.css('p')[0]

注意点として、cssメソッドでは該当する要素ノード全てが配列形式になって返ってきます。
たとえ、該当する要素ノードが1つしかなくても配列形式で返ってくるため、ノードに対して操作を行いたい時には必ずノード配列から取り出しましょう。

しかし、要素数が1つしかない時や先頭の要素のみが欲しい時にわざわざノード配列から取り出すのは面倒ですよね?そのためにNokogiriには以下のようなメソッドがあります。

#要素ノード(配列形式でない)が返る
doc.at('p')
doc.at_css('p')

3.3 テキストノードを取得する

doc.at_css('p').text
#=> "1つ目"

要素内のテキストノードを取得したい時にはtextメソッドを使います。
要素内に複数のテキストノードがある際にも、連結して返してくれます。

3.4 要素ノードの属性の値を取得する

doc.at_css('a').attribute('href').text
#=> "https://www.rooter.co.jp"
doc.at_css('a')[:href]
#=> "https://www.rooter.co.jp"

要素ノードの属性の値を取得する方法は

  • attributeメソッドで属性ノードを取得してから、textメソッドでテキストノードとして取得する方法
  • Hash(辞書型配列)のように取得する方法

の2つがあります。値を取得したいだけなら後者の方が簡潔で分かりやすいです。

3.5 要素ノードの配列を扱う

node_set = doc.css('#txts p')
node_set.each do |node|
  puts node.text
end

#=> 1つ目
#=> 2つ目

ノードを配列形式で取得したら、eachメソッドで各要素を順に操作することが可能です。

node_set = doc.css('p')
node_set.uniq #NoMethodErrorが起こる

注意点としてcssメソッドなどで返ってくるものは配列形式ですが、Ruby標準の配列ではありません。 そのため、普段配列に使うeach以外のメソッドを使うときは気をつけてください。

3.6 親子関係を利用して要素ノードを取得する

doc.at_css('#txts').children[0].text
#=> "1つ目"
doc.at_css('p').parent[:id]
#=> "txts"

Nokogiriでは親子関係を辿って、ノードを取得することも可能です。

  • 子ノードを取得したい時は、childrenメソッド
  • 親ノードを取得したい時は、parent メソッド

を使います。

このメソッドを紹介した理由として、スクレイピングでは自分がHTMLを作るわけではないので、どうしても指定しにくい要素があることもあります。その際にchildren・parentメソッドの存在を知っていると便利です。

4. 知ってると得する機能

4.1 DOMをHTMLに戻す

to_htmlメソッドは、指定した要素ノード以下の階層のHTMLを取得できます。
なお注意する点として、to_htmlメソッドで得られたHTMLとパース前のHTMLが一致するとは限りません。

doc.at_css('#txts').to_html
#=> "<div id=\"txts\">\n        <p>1つめ</p>\n        <p>2つめ</p>\n      </div>"

4.2 要素ノード内のHTMLを取得する

inner_htmlメソッドは、指定した要素内部のHTMLを取得できます。

doc.at_css('#txts').inner_html
=> "\n        <p>1つめ</p>\n        <p>2つめ</p>\n      "

5. 最後に

いかがでしたか?
今回はスクレイピングに必要なメソッドを紹介しましたが、スクレイピング以外にも使えるNokogiriの機能が沢山あるので、 興味があれば公式ドキュメントを参照することをオススメします。
それでは、Nokogiriを使いこなして快適なスクレイピングライフをお過ごしください!