Selenium for Python find_elementの実行速度をざっくりと比較する

# はじめに

Seleniumで自動化に挑戦してみました。
個人的に使用するには十分なものが作れました。
しかし、実行速度が気になりました。
調べてみたらfind_elementを繰り返し実行するだけで結構な時間がかかるようです。
そこで、find_elementの使い方で実行速度が変わるのか、どの方法を使えばよいか調べてみることにしました。
あんまり厳密な方法ではなくてざっくりとした結果ですが記事にしてみます。

# 実行環境

Python 3.7.3
selenium 3.141.0
Google Chrome: 75.0.3770.100（Official Build） （64 ビット）
ChromeDriver 75.0.3770.90
Firefox: 67.0.4 (64 ビット)
geckodriver 0.24.0 ( 2019-01-28)

# 実験

## テストサイト

テストサイトとしてgoogleを選びました。
余計なものを省くと以下のようなhtmlになります。
これのinputタグをfind_elementを使って取得することとします。

```html:html
<html itemscope="" itemtype="http://schema.org/WebPage" lang="ja">
  <body jsmodel=" " class="hp vasq big" id="gsr">
    <div class="ctr-p" id="viewport">
      <div class="jhp big" id="searchform">
        <form class="tsf nj" action="/search" style="overflow:visible" id="tsf" method="GET" name="f" onsubmit="return q.value!=''" role="search">
          <div jsmodel="vWNDde" jsdata="MuIEvd;;Cjj2lE">
            <div jscontroller="mvYTse" class="A8SBwf" jsaction="DkpM0b:d3sQLd;IQOavd:dFyQEf;XzZZPe:jI3wzf;Aghsf:AVsnlb;iHd9U:Q7Cnrc;f5hEHe:G0jgYd;vmxUb:j3bJnb;R2c5O:LuRugf;qiCkJd:ANdidc;NOg9L:HLgh3;uGoIkd:epUokb;zLdLw:eaGBS;rcuQ6b:npT2md">
              <div class="RNNXgb" jsname="RNNXgb">
                <div class="SDkEP">
                  <div jscontroller="iDPoPb" class="a4bIc" jsname="gLFyf" jsaction="h5M12e;input:d3sQLd;focus:dFyQEf;blur:jI3wzf">
                    <input class="gLFyf gsfi" maxlength="2048" name="q" type="text" jsaction="paste:puy29d" aria-autocomplete="both" aria-haspopup="false" autocapitalize="off" autocomplete="off" autocorrect="off" role="combobox" spellcheck="false" title="æ¤œç´¢" value="" aria-label="æ¤œç´¢" data-ved="0ahUKEwjP1420wfziAhXIebwKHfF-A5oQ39UDCAQ">
                  </div>
                </div>
              </div>
            </div>
          </div>
        </form>
      </div>
  </body>
</html>
```

## 対象ドライバー

1. ChromeDriver (google chrome)
2. geckodriver (firefox)

## find_elementの引数

1. DevToolsから得られたXPath
2. 簡潔なXPath
3. 冗長なXPath
4. クラス名
5. Name属性
6. 簡潔なCSSセレクタ
7. 冗長なCSSセレクタ

## ソース

```python:python
import functools
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


def stopwatchPrint(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print("{}: {}".format(func.__name__, end - start))
    return result
  return wrapper


def test(driver, by, value):
  driver.get("https://www.google.com/")
  wait = WebDriverWait(driver, 30)
  wait.until(EC.visibility_of_element_located((By.ID, "viewport")))
  element = getElement(driver, by, value)
  # print(element)
  element.send_keys("虎の敷物")
  element.submit()


@stopwatchPrint
def getElement(driver, by, value, t=1000):
  print("{}, {}".format(by, value))
  for i in range(t):
    element = driver.find_element(by, value)
  return element


InputXpathByDevTools = "//*[@id='tsf']/div[2]/div/div[1]/div/div[1]/input"
InputXpath1 = "//input[@name='q']"
InputXpath2 = "//body[@id='gsr']/div[@id='viewport']/div[@id='searchform']/form[@id='tsf']/div[@jsmodel='vWNDde']/div[@class='A8SBwf']/div[contains(@class, 'RNNXgb')]/div[@class='SDkEP']/div[@class='a4bIc']/input[@name='q']"
InputClass = "gLFyf"
InputName = "q"
# InputCSS1 = "input.gLFyf"
InputCSS1 = "input[name=q]"
InputCSS2 = "body#gsr div#viewport div#searchform form#tsf div[jsmodel=vWNDde] div.A8SBwf div.RNNXgb div.SDkEP div.a4bIc input[name=q]"

if __name__ == '__main__':
  try:
    #driver = webdriver.Chrome('\\webdriver\\chromedriver_win32\\chromedriver.exe')
    driver = webdriver.Firefox('\\webdriver\\geckodriver-v0.24.0-win64')

    test(driver, By.XPATH, InputXpathByDevTools)
    test(driver, By.XPATH, InputXpath1)
    test(driver, By.XPATH, InputXpath2)
    test(driver, By.CLASS_NAME, InputClass)
    test(driver, By.NAME, InputName)
    test(driver, By.CSS_SELECTOR, InputCSS1)
    test(driver, By.CSS_SELECTOR, InputCSS2)

  except Exception as e:
    print("{}: {}".format(type(e), e))

  finally:
    time.sleep(2)
    driver.quit()

```

# 結果

数回繰り返した結果、引数を変えても実行速度は変わらないようです。
また、geckodriverの方が、ChromeDriverより実行速度が速いようです。

つまり、

- 引数には簡潔で分かりやすい記述をする
- 問題がないようならばgeckodriverを選ぶ

とすれば良いようです

## ChromeDriver

実行結果の抜粋
28秒前後で動作します。
ただし、初めに呼んだものが明らかに実行速度が速いです。
この引数を最後に呼んでみると同じように28秒程度で動作しますので引数の問題ではなくて実行順の問題のようです。
なにかソースに間違いがあるのかもしれません。

初稿では、ChromeとChromeDriverのバージョンが合っていなかったのですが合わせてみても結果は特に変わらなかったです。

```text
xpath, //*[@id='tsf']/div[2]/div/div[1]/div/div[1]/input
getElement: 19.230003356933594

xpath, //input[@name='q']
getElement: 25.615001678466797

xpath, //body[@id='gsr']/div[@id='viewport']/div[@id='searchform']/form[@id='tsf']/div[@jsmodel='vWNDde']/div[@class='A8SBwf']/div[contains(@class, 'RNNXgb')]/div[@class='SDkEP']/div[@class='a4bIc']/input[@name='q']
getElement: 29.41499948501587

class name, gLFyf
getElement: 29.305000066757202

name, q
getElement: 28.85899519920349

css selector, input[name=q]
getElement: 28.90398097038269

css selector, body#gsr div#viewport div#searchform form#tsf div[jsmodel=vWNDde] div.A8SBwf div.RNNXgb div.SDkEP div.a4bIc input[name=q]
getElement: 29.159016132354736
```

## geckodriver

実行結果の抜粋
10秒前後で動作します。
geckodriverは実行速度が速いためかsubmitの結果が表示されずに次に行ってしまいます。
しかし、実験の本質と関係ないので無視しました。
(time.sleepを入れる、画面遷移を確認する等で正常に機能します。)

```text
xpath, //*[@id='tsf']/div[2]/div/div[1]/div/div[1]/input
getElement: 9.75801420211792

xpath, //input[@name='q']
getElement: 9.892000198364258

xpath, //body[@id='gsr']/div[@id='viewport']/div[@id='searchform']/form[@id='tsf']/div[@jsmodel='vWNDde']/div[@class='A8SBwf']/div[contains(@class, 'RNNXgb')]/div[@class='SDkEP']/div[@class='a4bIc']/input[@name='q']
getElement: 11.554003238677979

class name, gLFyf
getElement: 10.823017835617065

name, q
getElement: 11.586012840270996

css selector, input[name=q]
getElement: 10.772995471954346

css selector, body#gsr div#viewport div#searchform form#tsf div[jsmodel=vWNDde] div.A8SBwf div.RNNXgb div.SDkEP div.a4bIc input[name=q]
getElement: 11.920017004013062
```

# 最後に

Seleniumは、大分癖がありますね。
ChromeDriverで動かしていましたがgeckodriverに変えることとします。
大分、実行速度が変わりそうです。
また、わざわざ冗長に書いていたのを簡潔にしようと思いました。
またやることが増えた(´・ω・`)

# 追記

既存のプログラムをgeckodriverにしたら6倍ほど速くなりました。(\*´ω｀\*)