35歳からの中二病エンジニア

社寺・鉄道・アニメを愛でるウェブ技術者の呟き

RubyのDockerイメージ上でSeleniumを動かす

Ruby界隈ではSeleniumを動かすネタが少ないので、最近作った環境について書いておこうと思う。

ChromiumとChromiumDriverを追加済みのDockerfileを作る

巷ではChromeとChromeDriverのバイナリーをcurl等でダウンロードしてインストールする方法が一般的だが、バージョンを合わせる必要があったり上手く動かなかったりとハマりがちだったりする。その点、DebianベースのLinuxにChromiumとChromiumDriverの組み合わせであればaptから簡単にインストールすることができる。RubyのDockerイメージも多くがDebianベースなので、以下の例のように簡潔な書き方ができる。

#
# Builder
#
FROM ruby:3.3.5-slim as builder

ENV TZ=Asia/Tokyo

WORKDIR /opt/project

COPY Gemfile Gemfile.lock ./

RUN apt-get update && \
    apt-get install -y \
        g++ \
        gcc \
        make

RUN bundle install

#
# Main
#
FROM ruby:3.3.5-slim

ENV TZ=Asia/Tokyo
ENV RAILS_ENV=production
ENV RAILS_LOG_TO_STDOUT=1

WORKDIR /opt/project

COPY --from=builder /usr/local/bundle /usr/local/bundle
COPY . .

RUN apt-get update && \
    apt-get install -y \
        chromium \
        chromium-driver

CMD ["sleep", "infinity"]

CapybaraからSeleniumを操作する

あとはGemfileに capybaraselenium-webdriver を追加しておき、以下のようなCapybaraの設定ファイルを用意すれば良い感じにSeleniumを動かすことができる。

# frozen_string_literal: true

require 'capybara'
require 'selenium-webdriver'

Capybara.register_driver :chromium do |app|
  version = Capybara::Selenium::Driver.load_selenium
  capabilities_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
  capabilities = ::Selenium::WebDriver::Chrome::Options.new.tap do |options|
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument('--window-size=1280x960')
    options.add_argument('--disable-infobars')
    options.add_argument('--disable-extensions')
    options.add_argument('--single-process')
    options.add_argument('--disable-dev-shm-usage')
    options.add_argument('--homedir=/tmp')
  end
  Capybara::Selenium::Driver.new(app,
                                 browser: :chrome,
                                 service: Selenium::WebDriver::Service.chrome,
                                 capabilities_key => capabilities,
                                 timeout: 120)
end