LoginSignup
55
69

More than 5 years have passed since last update.

Rubyを使って20分でスクレイピングの大元を作る

Last updated at Posted at 2018-07-05

今回はRubyのNokogiriというGemを使って、スクレイピングをするときの大元となる準備をまとめました。

対象読者

  • 大量のURLページに対してスクレイピングをしたいけど、やり方がわからない
  • Rubyでスクレイピングしてみたい人

環境

  • Ruby2.5.0

実装

  • プロジェクトのディレクトリ構造は以下の通りです。
  • Gemはvendor/bundle配下にインストールするようにします。
.
├── Gemfile
├── Gemfile.lock
├── main.rb
└── vendor/bundle/*

Gemインストール

  • 以下のGemを入れましょう。
  • 今回の主役となるnokogiriはもちろんですが、pryを入れることでデバッグが楽になります。
gem 'nokogiri'
gem 'pry-byebug'

スクレイピングの為にページのHTMLをNokogiri::HTML::Documentに変換する

  • では実際に動くコードを書いていきます。
  • Nokogiriでスクレイピングする為には、ページのHTMLを材料にして、Nokogiri::HTML::Documentというオブジェクトを作る必要があります
  • それを実現するのが、Nokogiri::HTML.parseメソッドです。
# htmlにはページのHTML(String型)が入る
Nokogiri::HTML.parse(html, nil, 'utf-8')
  • ページのHTMLをString型として取得する方法なのですが、Rubyのopen-uriを使う方法と、Shellのcurlを使う方法とがあります。
  • どちらでもいいのですが、以前open-uriだとうまく行かない場合があって、その時はcurlの方を使いました。
# open-uriの場合
require 'open-uri'
html = open(url) { |f| f.read }

# curlの場合はkconvを使ってUTF-8にすること
require 'kconv'
html = `curl -s #{url}`.toutf8
  • ではこれらを使って、「ページのURLを渡すと、Nokogiri::HTML::Document型にして返してくれるメソッド」を作りましょう。(今回はopen-uriを使います。)
require 'open-uri'

def setup_doc(url)
  charset = 'utf-8'
  html = open(url) { |f| f.read }
  doc = Nokogiri::HTML.parse(html, nil, charset)

  # <br>タグを改行(\n)に変えて置くとスクレイピングしやすくなる。
  doc.search('br').each { |n| n.replace("\n") }

  doc
end
  • これで第一ステップ完了です。

Nokogiri::HTML::Documentから、必要な情報を抽出する

  • ページをNokogiri::HTML::Document型にしたら、いよいよデータの抽出です。
  • データの抽出にはXPathというものを使います。(こちらに詳しく書いてありました!)
  • 具体例だとこんな感じになります。
# ページのNokogiri::HTML::Documentを取得。
doc = setup_doc(url)

# ページのタイトル名と、詳細ページへのURLを抽出する。
page_title = doc.xpath('div/h1').text
detail_url = doc.xpath('div/h2/a').attribute('href').value
  • RubyのXPathでよく使う書き方については後日また書きます。

CSVへの出力

  • 最後はCSVへの出力です。
  • CSVに出力するにはcsvライブラリを使います。
  • CSVのファイルを開き(なければ自動で作成されます)、そこに必要な情報を格納した配列をpushしていくことで、CSVとして出力することができます。(配列内の順番は必ず揃えましょう)
require 'csv'

# CSVヘッダー
headers = ['Title', 'Detail URL', 'Page URL']

CSV.open('target.csv', 'w') do |csv|
  # 一行目はCSVヘッダーにする
  csv << headers

  # 今回はデータが一つだけだが、for文とかでたくさんpushすることができる
  csv << [page_title, detail_url, url]
end

まとめ

  • 全体をまとめるとこういう感じになります。(main.rbに書きます)
  • 実装完了後、ruby main.rbを実行すれば、スクレイピングが実行されてCSVが出力されます。
require 'bundler/setup'
require 'nokogiri'
require 'open-uri'
require 'csv'

def setup_doc(url)
  charset = 'utf-8'
  html = open(url) { |f| f.read }
  doc = Nokogiri::HTML.parse(html, nil, charset)
  doc.search('br').each { |n| n.replace("\n") }
  doc
end

def scrape(url)
  doc = setup_doc(url)
  page_title = doc.xpath('div/h1').text
  detail_url = doc.xpath('div/h2/a').attribute('href').value

  [page_title, detail_url, url]
end

if __FILE__ == $0
  urls = [
    'https://www.example1.com',
    'https://www.example2.com',
    'https://www.example3.com'
  ]
  csv_header = ['ページタイトル', '詳細URL', 'URL']

  CSV.open('result.csv', 'w') do |csv|
    csv << csv_header
    urls.each do |url|
      begin
        csv << scrape(url)
      rescue
        # エラー処理
        # 例) csv << ['err', 'err', url]
      end
    end
  end
end
55
69
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
69