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


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

```html
<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は以下。

```py3
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の頭文字をうつこと。

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

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

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

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

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

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

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

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

```p3
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だけ変更してもダメだった。

```py3
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の部分もよくないとは思うのだが、具体的に何を待てばよいのか不明なので、このまま使っている。