5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

seleniumでJavaScriptを含むselect要素への対応方法

Posted at

目的

社内サイトの入力が大変なので、seleniumを使って自動化する。その中で、
以下のようなselect要素を選択したい時に、はまったのでそのメモ。

<select onchange="return (DropDownList.OnChange(this, event));" 
        onfocus="return (DropDownList.OnFocus(this, event));" 
        onblur="return (DropDownList.OnBlur(this, event));" 
        id="aaa" 
        scriptclass="DropDownList" 
        class="bbb" 
        wrapped="true" 
        direction="ltr" 
        viewdatanode="6" 
        formid="ccc" 
        originalid="ddd" 
        tabindex="0" 
        title="" 
        style="direction:ltr">
    <option value="" selected=""></option>
    <option value="1">Alice</option>
    <option value="2">Bill</option>
    <option value="3">Chris</option>
    <option value="4">Dan</option>
</select>

JavaScriptを使って要素を埋めるような形?そもそもoptionが最初から埋まっているので、何をJavaScriptでやっているのか、そのあたりはよくわかりません。

環境

OS: Windows10
Python: 3.6.4
selenium: 3.4.3
driver: ChromeDriver 2.40.565498

importは以下。

from selenium import webdriver
from selenium.webdriver.support import ui
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common import exceptions
import time

試み

最初にやったのは、単純に通常ブラウザで自分が操作するように、フォーカスしてoptionの頭文字をうつこと。

select_dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'aaa')))
select_dropdown.send_keys('A')

これは以下のエラーで失敗する。

selenium.common.exceptions.WebDriverException: Message: unknown error: cannot focus element

少し調べたところ、select要素を選ぶにはSelectというクラスを使うらしいので、トライ。

select_dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'aaa')))
select = Select(select_dropdown)
select.select_by_index(1)

これは以下のエラーで失敗。

raise NoSuchElementException("Could not locate element with index %d" % index)
# selenium.common.exceptions.NoSuchElementException: Message: Could not locate element with index 1

調べると、要素をクリックしないとoptionが選択できないケースがあるようなので試す。

select_dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'aaa')))
select_dropdown.click()
select_dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'aaa')))
select = Select(select_dropdown)
select.select_by_index(1)

これはPyCharmのデバッガで1行ずつ実行すると普通に成功するが、上からrunすると失敗する。ただし、失敗した後に要素を選択することができるので、try/exceptで無理やり回避してみる。あと、ExpectedCondition(EC)というのもちゃんと理解していなかったが、このタイミングで調べて一番適切に思われるものに変えた。ちなみに、try/exceptなしでECだけ変更してもダメだった。

time.sleep(5)
select_dropdown = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID,'aaa')))
select_dropdown.click()
try:
    select = Select(select_dropdown)
    select.select_by_index(1)
except exceptions.StaleElementReferenceException:
    select_dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located(
        (By.ID, 'aaa')))
    select = Select(select_dropdown)
    select.select_by_index(1)
except:
    pass

これはうまくいった。
ただし、このサイトは同じような入力個所が複数あり、連続で埋めてくると要素を取得できないケースがあったので、先頭にsleepを少し入れている。

まとめ

いったんクリックしたうえで、selectインスタンス化に一度失敗させてから、リトライするとうまくいった。おそらく詳しい人から見るともっとやり方があるのだろうけど、現時点ではこれでうまくいっているのでよしとする。
sleepの部分もよくないとは思うのだが、具体的に何を待てばよいのか不明なので、このまま使っている。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?