スクレイピングで正規表現の練習

今すぐ始めるスクレイピング

nokogiriとかのスクレイピング用ライブラリを使わずに標準ライブラリ'open-uri'だけでスクレイピングをします。HTMLソースを取得するだけならこれで十分。以下のサイトを参考にスクレイピングしたいサイトのHTMLソースをまるごと文字列で取得します。

Webスクレイピングを用いたデータ検索クローラ構築入門

今回はgoogleの検索結果からスクレイピングしましょう。
目標は「rubyで検索したときに上位10件で表示されるサイトの名前とURLを取得する」ことにします。

ruby」の検索結果ですから、対象となるURLは https://www.google.co.jp/search?q=ruby です。

require 'open-uri'

url = 'https://www.google.co.jp/search?q=ruby'
user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36'
charset = nil
html = open(url, "User-Agent" => user_agent) do |f|
  charset = f.charset
  f.read
end

これで変数htmlにrubyの検索結果ページのHTMLソースが文字列として入りました。ターミナルに表示させてみると確認できます。

puts html

HTMLソースを観察する

さて、取得したHTMLソースをじっとながめましょう。欲しい情報は検索結果のサイト名とURLです。よく観察すると、それらは次のようなタグにはさまれていることがわかります。

<h3 class="r">
  <a href="http://qiita.com/shinya_131/items/901b9c70642b1e1f9227" onmousedown="return rwt(this,'','','','6','AFQjCNHzHhSI4ItvhPxCsbo7S9GCq_aOCA','KefVZQP9Zr0hbcUI_2iqEQ','0CEAQFjAFahUKEwidk8Dtl_bIAhUG4aYKHWZVAyc','','',event)" target="_blank">
    Rubyの凄く面白い特徴をRailsのコードを例に解説 - Qiita
  </a>
</h3>

つまり、こんな感じです。

<h3 class="r"> 〜ここにURLとタイトルがある〜 </h3>


これで欲しい情報を抽出するために何をすれば良いかがはっきりしました。特定のh3タグにはさまれた部分を正規表現で取り出せばよいわけです。

正規表現のおさらい

Ruby正規表現をおさらいします。

基本はスラッシュでかこめば正規表現になります。
/ruby/
文字列の中にスラッシュが含まれる場合などには、いちいちバックスラッシュでエスケープするのがめんどうなので、
%r{ruby}
という書き方もできます。今回は後者の書き方でいきましょう。

文字列の中から正規表現にマッチする文字列を抽出するにはString#scanメソッドを使います。正規表現に()を使うと、マッチするパターンの中で()内を並べた配列を返します。
たとえばこうです。

# <b></b>に囲まれた文字を取り出す
str = "this is a <b>car</b>."
p str.scan(%r{<b>(.+?)</b>})
# => [[car]]
# 二次元配列になっていることに注意

かっこ()内の意味は
. … 任意の一文字
+ … 1回以上の繰り返し
? … 最小マッチ
です。
最小マッチがすこし分かりにくいですが重要です。これがないと最長マッチをするのでおかしなことになります。
たとえば、「<b>you</b> are a <b>dog</b>」という文字列から"<b>"と"</b>"ではさまれた部分を取り出そうとして、「you</b> are a <b>dog」を取り出してしまったりします。

google検索結果からタイトルとURLを抽出する

以上を踏まえてこんなコードを書きます。

# <h3 class="r">-ここにはさまれた文字列-</h3>を集める
strs =  html.scan(%r{<h3 class="r">(.+?)</h3>})

# <a>タグの中のhref属性とタイトルを抜き出す
for i in 0...strs.length do
  url, title = (strs[i][0].scan(%r{<a href="(.+?)".+?>(.+?)</a>}))[0]
  puts "#{title} #{url}"
end

実行結果が以下のような感じです。(記事執筆時点)

Ruby - ウィキペディア https://ja.wikipedia.org/wiki/Ruby
オブジェクト指向スクリプト言語 Ruby https://www.ruby-lang.org/ja/
Ruby - ウィキペディア https://ja.wikipedia.org/wiki/Ruby
Rubyにはウンザリ!動的型付け、副作用、およびオブジェクト ... http://postd.cc/sick-of-ruby-dynamic-typing-side-effects-object-oriented-programming/
Ruby入門 - RubyとRailsの学習ならRubyLife http://www.rubylife.jp/ini/
Rubyの凄く面白い特徴をRailsのコードを例に解説 - Qiita http://qiita.com/shinya_131/items/901b9c70642b1e1f9227
Ruby - 【初心者】生まれて初めてプログラミングをする文系人間 ... http://qiita.com/yoshimikeisui/items/0872739e771f8a18a956
一ヶ月、Rubyを本気で独学した結果(文系向け) - 30ruby記 http://30ruby.hatenablog.com/entry/2014/11/01/030505
Ruby on Railsをはじめよう - OpenBook http://openbook4.me/projects/92/sections/485
Rubyとは - はてなキーワード - はてなダイアリー http://d.hatena.ne.jp/keyword/Ruby
Ruby 3.0の未来へ―、言語設計者のまつもと氏が示す3つの ... http://hrnabi.com/2015/05/12/7035/