Nokogiriについて学ぶ
はじめに
webスクレイピングについて調べたところ、「nokogiri」というgemの情報が出てきた。
そこで、nokogiriに実際に触って理解を深めた。
できること
公式のhow toの冒頭に「Nokogiri is a large library, and so it's challenging to briefly summarize it. We've tried to provide long, real-world examples at Tutorials .」とある。
翻訳すると、「Nokogiriは大きなライブラリなので、簡単にまとめるのは難しいです。チュートリアルで長い実例を提供するようにしました。」
どうやらたくさんの機能を備えたライブラリのようだ。
公式の一例
#! /usr/bin/env ruby
require 'nokogiri'
require 'open-uri'
# HTMLを取得し、パースする
doc = Nokogiri::HTML(URI.open('<https://nokogiri.org/tutorials/installing_nokogiri.html>'))
# CSSでノードを検索する
doc.css('nav ul.menu li a', 'article h2').each do |link|
puts link.content
end
# xpathでノードを検索する
doc.xpath('//nav//ul//li/a', '//article//h2').each do |link|
puts link.content
end
# 混ぜて使用する
doc.search('nav ul.menu li a', '//article//h2').each do |link|
puts link.content
end
上の例を手元のirbで試してみる。
HTMLを取得し、パースする
irb(main):004:0> doc = Nokogiri::HTML(URI.open('<https://nokogiri.org/tutorials/installing_nokogiri.html>'))
=> #<Nokogiri::HTML4::Document:0x148d4 name="document" children=[#<Nokogiri::XML::DTD:0x2d0 name="html">, #<Nokogiri::XML::Element:0x148c0 name="html" attributes=[#<Nokogir...
irb(main):005:0>
なんだかよくわからないオブジェクトが返ってきている。
まずこれを知るためにNokogiri::HTML
とURI#open
について調べる。
open-uri公式:https://docs.ruby-lang.org/ja/latest/library/open=2duri.html
Nokogiri公式APIドキュメント:https://nokogiri.org/rdoc/index.html
-
URI#open
- 公式によると、openの引数にhttp:// や https://、ftp:// で始まっている文字列を渡すと、 URI のリソースを取得した上で StringIO オブジェクトまたは Tempfile オブジェクトとして返すらしい。
irb(main):005:0> doc = URI.open('<https://nokogiri.org/tutorials/installing_nokogiri.html>') => #<Tempfile:/var/folders/lr/fmfvf78j5dz5typy3qlwnp6r0000gn/T/open-uri20221101-44816-fouxid>
- たしかにTempfileオブジェクトとして返ってきている。Tempfileオブジェクトについて調べてみると、「Tempfile オブジェクトは**FileクラスへのDelegatorとして定義されており、File**クラスのオブジェクトと同じように使うことができます。」とある。
- ためしに
IO#read
を使って中身を文字列で確認してみると
irb(main):009:0> doc.read => "\\n<!doctype html>\\n<html lang=\\"en\\" class=\\"no-js\\">\\n <head>\\n \\n <meta charset=\\"utf-8\\">\\n <meta name=\\"viewport\\" content=\\"width=device-width,initial-scale=1\\">\\n \\n <meta name=\\"description\\" content=\\"The Official Tutorial Archive™ of Nokogiri®\\">\\n \\n \\n <meta name=\\"author\\" content=\\"Mike Dalessio\\">\\n \\n \\n <link rel=\\"canonical\\" href=\\"<https://nokogiri.org/tutorials/installing_nokogiri.html\\>">\\n 長いので以下略
- となっていて、htmlリソースが入っていることがわかる。
-
Nokogiri::HTML
- HTMLとしてパースし、Nokogiri::HTML4::Documentオブジェクトを返す。(ちなみにNokogiri::HTML.parseとしても同じ結果になる。)
- Nokogiri::HTML4::Documentオブジェクトに対して様々なメソッドを使うことで、目的のノードを取得したり、ドキュメントのtitleを確認したりできる。
irb(main):009:0> doc = Nokogiri::HTML(URI.open('<https://nokogiri.org/tutorials/installing_nokogiri.html>')) => #<Nokogiri::HTML4::Document:0x3d4dc name="document" children=[#<Nokogiri::XML::DTD:0x28eec name="html">, #<Nokogiri::XML::Element:0x3d4c8 name="html" attributes=[#<Nokog... irb(main):010:0> puts doc <!DOCTYPE html> <html lang="en" class="no-js"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="description" content="The Official Tutorial Archive™ of Nokogiri®"> <meta name="author" content="Mike Dalessio"> <link rel="canonical" href="<https://nokogiri.org/tutorials/installing_nokogiri.html>">
- Nokogiri::HTML4::Documentオブジェクトにputsを使うと、こんな感じでちゃんとHTMLとしてパースされてるのがわかる。
ノードを検索する
- https://nokogiri.org/tutorials/searching_a_xml_html_document.html
- xpathやCSSのクエリを使って検索する。
- ためしに自作サービスのうつメイトのトップページから文言を抜き出してみる
doc = Nokogiri::HTML(URI.open('<https://utsumate.net>'))
css = doc.css('#wrapper__start > main > div > p.mb-6.top-page-content-line-height')
css.text
=> "このアプリは、そんな思いに応えるために作られました"
- とれた。
上記のやりかたでは情報を取得できないサイトも
ここまでで学んだやりかたで、ためしに発売スケジュール|任天堂の発売予定のゲームタイトルの情報をとってみようとすると…。なぜかとることができない。
doc = Nokogiri::HTML(URI.open('<https://www.nintendo.co.jp/schedule/index.html>'))
puts doc
として中身を確認するとわかるが、ゲームに関する情報が丸々抜けている。
なぜこんなことが起こるかというと、サイトによっては、javascriptによってindex.htmlとは別のhtmlを取得するリクエストが投げられていたり、コンテンツの一部がJSONから取得されていたりするからだ。
参考:RubyでWebスクレイピング \#5 javascript対応テクニック
こういう場合はこれまでと違ったアプローチを取らなければいけない。
次はそういったアプローチについて学んでいきたい。
感想
webスクレイピングで情報をとれるようになれば、作れるものの幅も広がるし、実生活でも役立つと思うので、引き続き学んでいきたい。