【Ruby】anemoneとnokogiriでクローラーを作ってみた。


システム開発にあたり、他のWEBページから情報を取得する必要があったので、クローラーを作成しました。

 最初に考えたこと

私がやろうとしていたことは、他のWEBページから情報を取得し、現在作成中のアプリのDBに保存することでした。ということで、なんとなく「情報を取得」ってことはスクレイピングか!という感じで意気込んでいました・・・。

 調査を開始し、分かったこと

自分がやろうとしていたことは、確かにスクレイピング。しかしながら、その情報を取得するためには、まずクローラーと呼ばれるものを作成しなければならないことが判明しました。

クローラーとは?

一方クローラーは、Webページ内にある全てのリンクを巡回して、深堀りしながら目的の情報を取得する方法です。この行為自体はクローリングと呼ばれます。
クローラーでリンクを探す際にはもちろんスクレイピングをしてHTMLのタグを解析して、リンク先を取得します。

スクレイピングとは?

スクレイピングとは、WebページのHTMLを解析してデータを抽出することです。

要するに、
●スクレイピングは1つのページに情報が集まっている場合に使える。
●クローラーはWEBサイト内を巡回してくれる。

ということで、クローラーを作成し→その中にスクレイピングに関するコードを記述する→DBに保存する記述を書くという流れが見えてきました。

完成したソースコード

# ruby gemを使用 nokogiri anemoneを使えるようにした
require 'nokogiri'
require 'anemone'
require 'pry'
# 巡回起点となるURL
URL = 'https://********/********'.freeze

area_urls = []
prefecture_urls = []
city_urls = []
# サイト内を巡回する記述
Anemone.crawl(URL, depth_limit: 0, delay: 1) do |anemone|
  anemone.focus_crawl do |page|
    page.links.keep_if do |link|
      link.to_s.match(%r{*********/[0-9]{1,2}})
    end
    page.links.each do |link|
      area_urls << link
    end
  end
end

area_urls.each do |area|
  Anemone.crawl(area, depth_limit: 0, delay: 1) do |anemone|
    anemone.focus_crawl do |page|
      page.links.keep_if do |link|
        link.to_s.match(%r{**********/[0-9]{1,2}/[0-9]{5}})
      end
      page.links.each do |link|
        prefecture_urls << link
      end
    end
  end
end

prefecture_urls.each do |prefecture|
  Anemone.crawl(prefecture, depth_limit: 1, delay: 1, skip_query_strings: true) do |anemone|
    anemone.focus_crawl do |page|
      page.links.keep_if do |link|
        link.to_s.match(%r{**********/[0-9]{1,2}/[0-9]{5}/[0-9]})
      end
      page.links.each do |link|
        city_urls << link
      end
    end

    PATTERN = %r[**********/[0-9]{1,2}/[0-9]{5}/[0-9]].freeze

    anemone.on_pages_like(PATTERN) do |page|
      url = page.url.to_s

      str = url.to_s

      html = URI.parse(url).open
      # ここからスクレイピングの記述
      doc = Nokogiri::HTML.parse(html, nil, 'UTF-8')
# XpathでHTMLを指定
      name = doc.xpath('/html/body/div[4]/div/div[2]/div[1]/h1').text
      pos = doc.xpath('/html/body/div[4]/div/div[2]/table[1]/tbody/tr[3]/td/text()[1]').text
      post = pos.strip
      postcode = post.match(/[0-9]{7}/)
      add = doc.xpath('/html/body/div[4]/div/div[2]/table[1]/tbody/tr[3]/td/text()[2]').text
      address = add.strip
      tel = doc.xpath('/html/body/div[4]/div/div[2]/table[1]/tbody/tr[4]/td').text
      fax = doc.xpath('/html/body/div[4]/div/div[2]/table[1]/tbody/tr[5]/td').text
      staff_number = doc.xpath('/html/body/div[4]/div/div[2]/table[4]/tbody/tr[1]/td/p').text
      company = doc.xpath('/html/body/div[4]/div/div[2]/table[5]/tbody/tr[2]/td').text
      office_url = doc.xpath('/html/body/div[4]/div/div[2]/table[1]/tbody/tr[6]/td/a').text
      # 正規表現でURLから5桁の数字を抜き出す
      if str =~ %r{/(\d{5})(/|$)}
        city_number = Regexp.last_match(1)
        p Regexp.last_match(1)
      end
# インスタンスを作成し、スクレイピングで取得した情報をDBに保存、その際にバリデーションを無視する。
      offices = Office.new(name: name,
                           postcode: postcode,
                           tel: tel,
                           fax: fax,
                           address: address,
                           staff_number: staff_number,
                           company: company,
                           url: office_url,
                           city_number: city_number)
      offices.save(validate: false)
    end
  end
end