【Rails】Webスクレイピング機能の実装

プログラミング

背景

Railsで作成中のオリジナルアプリに以下の機能を実装したかった。

あるサイトのURL(URI)を登録(フォーム送信)すると、そのサイトのHTML文書のhead部分にある<title>、厳密には<title>と</title>との間の文章(文字列)を抽出する機能

例えば、Yahooのトップページ(https://www.yahoo.co.jp/)の場合、<title>には”Yahoo! JAPAN”と記載されている。


やりたいことは、フォームからhttps://www.yahoo.co.jpというURLを入力するだけで、<title>と</title>との間の”Yahoo JAPAN”の文字列だけ(以下、単に「タイトル」)を自動的に抽出すること。

解決手段

まずは、スクレイピング用のGemであるMechanizeをインストール。

Gemファイルに gem ‘mechanize’ を追記して、bundle install。

やり方は他にも色々あると思うが、ここではHelperに以下のように記載。

require 'mechanize'

module HogeHelper

  def scrape_title
    agent = Mechanize.new
    page = agent.get(@hoge.url)
    element = page.at('title')
    str = element.to_s
    title = str.gsub!(/<.+?>/, '') 
  end
end

@hoge.urlは、登録したサイトのURLが入っている。

page.atで最初に登場するtitleタグを取得(基本的には一つしかないと思うが)。

element.to_sで文字列に変換して、gsub!で<と>とで囲まれた文字列(<title>と</title>)を除去(正確には”で置換)。

/<.+?>/は正規表現で、<から始まって>で終わる文字列を意味している。

<title>と</title>とで、それぞれ削除(置換)処理すれば良いかと考えていたが、色んなサイトを検証したところ<title class=”…”>とclass属性が付いているtitleタグもあったので、正規表現でこれもカバー。

ApplicationControllerでHogeHelperをinclude。

class ApplicationController < ActionController::Base
  
  include HogeHelper
  
end

HogeControllerのcreateアクションで下記のように記載すると、@hoge.nameにスクレイピングしてきたタイトルが入る。

  def create
    @hoge = Hoge.new(hoge_params)
    if @hoge.save
      @hoge.name = scrape_title
      @hoge.save
      flash[:success] = 'hogeを登録しました'
      redirect_to @hoge
    else
      flash.now[:danger] = 'hogeの登録に失敗しました'
      render :new
    end
  end

注意すべきは、一般的によくやる、hogeモデルファイル(hoge.rb)の:nameバリデーションでpresence: trueとすると、エラーになる。

:nameはHelperメソッドから取ってくるため、この時点では:nameが無くてもhogeインスタンスを生成できるようにする必要がある。

用途によって、抽出したタイトルを変更されたくないときは、ストロングパラメータに:nameを含めない。

もっと良いやり方もありそうだが、ひとまず上記でもできたのでメモ。