はじめに

スクレイピングをご存じですか?
スクレイピングは、ウェブページなどから情報を抜き出すことをいいます。
近年では、ビッグデータや機械学習などの人気の高まりにより、大量のデータを必要としています。
今ではウェブ上には1ゼタバイト(約1兆ギガバイト)以上のデータが存在すると言われています。
スクレイピングを行えば、大量のデータを入手できることが想像できるでしょう。
この記事では、Ruby初心者の方向けに、Rubyによるウェブページのダウンロード方法とスクレイピングについてお伝えしていきます。
順を追って説明していくので、ぜひコードを書きながら読んでみて下さい。

1:ウェブページをダウンロードする

まずは、スクレイピングの対象となるウェブページをダウンロードすることから始めましょう。
ここでは、Yahoo! JAPANのトップページを取得して、現在のニュースの一覧と本文を取得するプログラムを考えてみます。
ページのダウンロードは、次のようにとても簡単です。
例:

require 'open-uri'
top_page = open("https://www.yahoo.co.jp/")
top_page_html = top_page.read
top_page.close
puts top_page_html

Rubyには、標準ライブラリとして「open-uriモジュール」が組み込まれています。
これを使えば、例のようにわずか3行でウェブページがダウンロードできます。
HTMLが出力されていることが確認できますね。
次に、このHTMLからニュースのURLを抽出してみましょう。

2:URLの抽出

次に、先ほど取得したトップページのHTMLからニュースのURLを抽出してみます。
ニュースのURLはユニークなため、次のように正規表現を使うと簡単に抜き出せます。
例:

require 'open-uri'
top_page = open("https://www.yahoo.co.jp/")
top_page_html = top_page.read
top_page.close
news_urls = top_page_html.scan(/https:\/\/news\.yahoo\.co\.jp\/pickup\/[0-9]+/)
puts news_urls

例はかなり安直な正規表現ですが、目的を達するには必要十分です。
正規表現中の特殊文字を「\」でエスケープすることを忘れないようにしましょう。
さて、ニュースのURLは取得できましたが、このURLではニュースの本文を得られません。
そのため、次のようにさらにニュースの詳細のURLを取得する必要があります。
例:

news_detail_urls = news_urls.map do |url|
    news_page = open(url)
    news_page_html = news_page.read
    news_page.close
    news_page_html.match(/<a .*href="(.+?)".*>\[.+\]<\/a>/)[1]
end
puts news_detail_urls

この例では、「mapメソッド」を使って、配列(news_urls)の要素を別の配列(news_detail_urls)へと変換しています。
処理内容としては、それぞれのニュースのHTMLを取得し、そこからニュースの詳細のURLを抜き出しています。
正規表現に使われている「()」はグループ化といって、これを使うと「matchメソッド」から返されるマッチオブジェクトで、カッコ内でマッチした文字列にアクセスできます。
実行すると、ニュースの詳細のURLのみが返ってくることが確認できますね。

3:HTMLの解析

ここまで、HTMLからURLを抜き出してきました。
最後に、ニュースの詳細のHTMLから「記事タイトル」と「記事本文」を抜き出してみましょう。
次の例をみて下さい。
例:

news_detail_urls.each do |url|
    news_detail_page = open(url)
    news_detail_page_html = news_detail_page.read
    news_detail_page.close
    title = news_detail_page_html.match(/<h1>(.+)<\/h1>/m)[1]
    body = news_detail_page_html.match(/<p class="ynDetailText">(.+?)<\/p>/m)[1]
    puts "■#{title}--------------------------------------------------"
    puts body
end

流れ的には、URLを抜き出す処理とほとんど同じです。
注意しておきたいのは、HTMLには改行が含まれていることがあることです。
正規表現はデフォルトで、行単位で処理されます。
つまり、マッチするはずの文字列の途中に改行が含まれているとマッチしなくなってしまうのです。
このため、改行が含まれる文字列に対するマッチングには、正規表現に”m”オプションをつけます。
これは、例のようにパターン文字列の後ろに記述すればOKです。
これでスクレイピングは完了です。
とはいえ、ウェブ上の情報は変更されるのが常ですから、今までうまくいっていたものが動かなくなることもあります。
その都度、対象のHTML似合わせて修正していきましょう。

まとめ

Rubyによるスクレイピング方法がお分かりになりましたか?
今回は標準ライブラリだけを使って、HTML解析部分は独自に行いました。
単純なHTMLなら問題ありませんが、複雑なHTMLではかなり面倒です。
そんなときは、外部ライブラリのHTMLパーサー(Nokogiriなど)を使うと楽にできます。
根底にある仕組みを理解したら、ライブラリに頼ってもよいでしょう。