はじめに
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 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>
対象ドライバー
- ChromeDriver (google chrome)
- geckodriver (firefox)
find_elementの引数
- DevToolsから得られたXPath
- 簡潔なXPath
- 冗長なXPath
- クラス名
- Name属性
- 簡潔なCSSセレクタ
- 冗長なCSSセレクタ
ソース
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のバージョンが合っていなかったのですが合わせてみても結果は特に変わらなかったです。
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を入れる、画面遷移を確認する等で正常に機能します。)
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倍ほど速くなりました。(*´ω`*)