YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

画面外にあるダウンロードボタンを押してファイルを取得する

はじめに

日常的にやってる作業を自動化するためにSeleniumを使っているわけだけど、どうでもいいことで無駄なハマり方をしたのでメモしておく。基本的にChrome Driverしか使ってないので他のブラウザの挙動は知らない。

画面外にあるボタンをクリックする

「要素がクリックできる状態になったらクリックする」というコードを最初適当に書いていたけれど、それだとエラーが出てクリックできなかったので、なぜかなーと思っていた。 たとえばこういう感じでコードを書いて実行すると

button_path = '//path/to/xpath'
WebDriverWait(driver, 10).until(
    expected_conditions.elements_to_be_clickable(
        (By.XPATH, button_path)
    )
)
button = driver.find_element_by_xpath(button_path)
button.click()

次のようなエラーが出ていた。

Traceback (most recent call last):
...
selenium.common.exceptions.WebDriverException: Message: unknown error: Element is not clickable at point (61, 6703)
  (Session info: chrome=61.0.3163.100)
  (Driver info: chromedriver=2.30.477690 (c53f4ad87510ee97b5c3425a14c0e79780cdf262),platform=Mac OS X 10.12.6 x86_64)

viewport外に要素がある場合にこういう挙動になる模様。ActionChainsで対応できるかなと思って次のようなコードに変更。

button = driver.find_element_by_xpath(button_path)
actions = ActionChains(driver)
actions.move_to_element(button)
actions.click()
actions.perform()

エラーは出なくなったけどダウンロード部分がうまくいかない。

ダウンロード用の設定と画面の調整

Chromeの起動オプションの変更

SeleniumでのChrome起動時にオプションを追加してダウンロードの際のプロンプトなどを滅殺する。

options = selenium.webdriver.ChromeOptions()
download_dir = os.path.dirname(__file__)
options.add_experimental_option("prefs", {
    "download.default_directory": download_dir,
    "download.prompt_for_download": False,
    "download.directory_upgrade": True
})
driver = selenium.webdriver.Chrome(chrome_options=options)

画面を強制的にスクロールする

ActionChains#move_to_element はマウスをそこまで持ってってくれるだけで画面のスクロールをしてくれないので(どうもviewport内のクリックでしかダウンロードイベントが発生しない)、仕方なくJavaScriptを無理やり実行して動かす。

driver.execute_script("arguments[0].scrollIntoView()", button)