selenium/standalone-chromeでheadlessでリモートから接続で、且つダウンロードもしたい


関連図

selenium/standalone-chrome

こういう感じで繋がっている場合、chromeにダウンロードさせても操作しているruby側にはファイルがダウンロードされるわけではない(ダウンロードされるにしてもselenium/standalone-chromeのコンテナ側に入る)。

解決方法

ボリュームを使う

envにパスを書いておくことで、docker-compose.yml、ruby側(ENV["CHROME_DOWNLOAD_DIR"])で同じ値が参照できる。

.env
CHROME_DOWNLOAD_DIR=/chrome/downloads
docker-compose.yml
# .envがあればdocker-composeはとりあえずそれを読んでymlに反映してくれる
services:

  # /chrome/downloads
  # を共通のボリュームにしておく
  # ただそれだけだと権限の問題で書き込めないので、適当に777とかにしておく
  # $ sudo chmod 777 chrome/downloads
  chrome:
    image: selenium/standalone-chrome
    shm_size: 256m
    volumes:
      - .${CHROME_DOWNLOAD_DIR}:${CHROME_DOWNLOAD_DIR}

  backend:
    env_file:
      - .env # ファイルを指定
    build:
      context: ./backend
    working_dir: /app
    volumes:
      - ./backend:/app
      - .${CHROME_DOWNLOAD_DIR}:/app${CHROME_DOWNLOAD_DIR}

headless chromeでファイルをダウンロードする方法

一般的なseleniumの使い方ならたしかにdownload_path=入っているが、リモートだとそもそもこのモジュールが入っていないので、下のように手を加える必要がある。

Selenium::WebDriver::Chrome::Driver Selenium::WebDriver::Remote::Driver
include DriverExtensions::DownloadsFiles あり なし
...
...

driver = Capybara::Selenium::Driver.new(
  app,
  browser: :remote,
  desired_capabilities: caps,
  url: "http://#{DOCKER_CHROME_SELENIUM_HOST_NAME}:4444/wd/hub"
)
bridge = driver.browser.send(:bridge)
bridge.http.call(
:post,
"session/#{bridge.session_id}/chromium/send_command",
cmd: "Page.setDownloadBehavior",
params: { behavior: "allow", downloadPath: ENV["CHROME_DOWNLOAD_DIR"] }
)

後は/app/chrome/downloadsにファイルが入ってくるのがruby側でも見れる。