Chrome+Seleniumでファイルをダウンロードできない【解決済】

これは、Selenium + Python で Chrome を自動操作してファイルをダウンロードしようと思ったときの話です。テストでは上手くいくのに本番環境にいくとダウンロードされない。エラーも発生しない。ぜんぜん分からず調べること数時間…。

結局原因はヘッドレスモードでした。本番時にのみ--headlessオプションをつけていたことが仇になりました。Chrome の初期設定では(セキュリティのためか)ヘッドレスモードではファイルをダウンロードできないようです。それならそれでエラーで落としてほしいものです。

Webにあった情報を参考にプログラムを修正したら解決。無事にダウンロードができました。

ヘッドレスモードでのダウンロード方法

まずは必要なパッケージのインポート。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

次にオプションの生成。パス関係はそれぞれの環境に応じて適宜変更してください。--headlessオプションでヘッドレスモードを指定します。

opt = Options()
exec_path = '/usr/bin/chromedriver'
opt.binary_location = '/usr/bin/google-chrome'
## Windows の場合
#exec_path = 'C:\\bin\\chromedriver_win32\\chromedriver.exe'
#opt.binary_location = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
opt.add_argument('--headless')

次にダウンロードに関する詳細設定を生成します。download.default_directoryという保存先を指定する項目もありますが、これは後から別の方法で指定するので、ここでは不要です。ここでやっているのは簡単に言うと「保存先を毎回確認しない」ようにしつつ「選択した保存先が保持される」ようにしています。

prefs = {}
prefs['download.prompt_for_download'] = False
prefs['download.directory_upgrade'] = True
opt.add_experimental_option('prefs', prefs)

ここまでやってとりあえずChromeを起動します。

driver = webdriver.Chrome(chrome_options=opt, executable_path=exec_path)

そして本題のダウンロードの有効化です。ここではchromedriverに直接コマンドを投げます。download_pathはダウンロードファイルの保存先ディレクトリーの絶対パスです。Seleniumでダウンロードする場合は都度保存パスを指定することはできませんので、こうしてデフォルトのパスを指定します。残念ながら保存するファイル名も指定することができません。

driver.command_executor._commands["send_command"] = (
  'POST',
  '/session/$sessionId/chromium/send_command'
)
driver.execute(
  "send_command",
  params={
    'cmd': 'Page.setDownloadBehavior',
    'params': { 'behavior': 'allow', 'downloadPath': download_path }
  }
)

これでヘッドレスモードでもファイルのダウンロードができるようになりました。