Rails Nokogiriを使用する

Nokogiriのインストールについては、Rails Nokogiri を導入するを参照ください。


リファレンス


リファレンスの見方

Nokogiri::HTMLメソッドなどを見ると、戻り値が=> Objectとなっており、戻り値の型が分かりません。そんなときは、戻り値に対して、.classメソッドを使い、型を確認します。例えば次のようにします。

doc = Nokogiri::HTML(open('http://www.yahoo.co.jp'))
p doc.class
#=> Nokogiri::HTML::Document

上の例では、戻り値がNokogiri::HTML::Documentであることが分かるので、リファレンスのNokogiri::HTML::Documentのページを参照して、どんなメソッドが使えるのか確認できます。

さらに上記のページを見ると、XML::Documentを継承(Inherits)しており、さらにXML::Nodeを継承していることが分かるので、そちらのメソッドも使えます。


基本的な使いかた

Nokogiriは次のステップで使います。

  1. Nokogiriライブラリを読み込む
  2. HTMLなどを解析(パース)して、Nokogiriドキュメントを生成する。
  3. CSS指定かXPath指定で目的の要素を取得する。

Step1. Nokogiriライブラリを読み込む

Nokogiriを使うには、nokogiri を読み込みます。

require 'nokogiri'

Step2. Nokogiriドキュメントを生成する

HTMLを解析してNokogoriドキュメントを生成するにはHTMLメソッドを使います。open-uriはhttp/ftpに簡単にアクセスするためのクラスです。openメソッドがopen-uriのものです。

require 'open-uri'
doc = Nokogiri::HTML(open('取得元のURL'))

ここで得られたdocは、Nokogiri::HTML::Document(継承:XML::NodeXML::DocumentXML::Searchable) です。使えるメソッドはリンク先のリファレンス参照。

カラム:ブラウザでの見た目通りにHTMLを取得できない

例えば、www.yahoo.co.jpをopenメソッドで取得すると、ブラウザでアクセスした場合と結果が異なります。次のようにすると確認できます。

doc = Nokogiri::HTML(open('http://www.yahoo.co.jp'))
render :text => doc.to_html

この場合はユーザーエージェントを指定してアクセスします。例としてgoogle chromeのユーザーエージェントを使用してみます。

doc = Nokogiri::HTML(open('http://www.yahoo.co.jp',
 "User-Agent" => "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")

ブラウザと同じように取得できたことを確認します。

Step3. ドキュメントから目的の要素を取得する

XML::Searchableのメソッドを使って絞り込みを行います。

方法1.

CSSで検索する場合は、cssメソッドを使います。cssメソッドの戻り値はXML::Elementの集合のXML::NodeSetです。

doc.css('CSSセレクタルール')

例) IDが#topicsfbのリスト要素liを取得するには次のようにします。

nodes = doc.css('#topicsfb li')

# 取得したXML::NodeSetからXML::Elementを取り出して表示します。
nodes.each do |node|
 p node.text
end

方法2.

XPathで検索する場合は、xpathメソッドを使います。

doc.xpath('XPath')

例) IDが#topicsfbのリスト要素liを取得するには次のようにします。cssメソッドの戻り値はXML::Elementの集合のXML::NodeSetです。

nodes = doc.xpath('//*[(@id = "topicsfb")]//li')

# 取得したXML::NodeSetからXML::Elementを取り出して表示します。
nodes.each do |node|
 p node.text
end