🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

Q&A

解決済

3回答

9446閲覧

rescueを使用すると実行時間が10倍近く長くなる

yuki_90453

総合スコア326

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

0グッド

0クリップ

投稿2016/11/03 10:16

編集2016/11/08 07:41

###前提・実現したいこと
Rubyにおいてエラーが起きた際、プログラミングを止めない為にrescueを追加しました。
本来60秒程度で終わっていたプログラムが、640秒近くまで動作を終了するのに時間が掛かってしまいます。

考察ですが、rescueを使用する事によってタイムアウトするまで動作を繰り返しているのではないかと考えております。
使用している事コードは下記になります。

###該当のコード

def point(i) if i.find_element(:xpath => ".//th[contains(text(),'ポイント利用')]").displayed? == true i.find_element(:xpath => ".//th[contains(text(),'ポイント利用')]/following-sibling::td").text() end rescue Selenium::WebDriver::Error::NoSuchElementError return "なし" end

###質問
rescueの使用法が間違っているのでしょうか?
また、もしタイムアウトをするまで動作を繰り返していた場合、タイムアウト時間を短くする方法または、繰り返させない方法を教えて頂けれないでしょうか
宜しくお願い致します。

###編集部分
ボトルネックになっている部分は条件分岐で使用しているelement_present?メソッドまたは、point_nextTDメソッドかと思います。
計測結果、element_present?にてrealが平均20秒掛かっています。
このrealの待機時間を解消する方法はないでしょうか?

###ベンチマークの実施結果
該当のコード

Ruby

1 def point(i) 2 if element_present?(i, :xpath, ".//th[contains(text(),'ポイント利用(')]") == true 3 point_nextTD(i) 4 else 5 return " " 6 end 7 end 8 9 def element_present?(where, how, what) 10 Benchmark.bm 10 do |r| 11 r.report "条件分岐" do 12 begin 13 where.find_element(how => what) 14 true 15 rescue Selenium::WebDriver::Error::NoSuchElementError 16 false 17 end 18 end 19 end 20 end 21 22 def point_nextTD(i) 23 Benchmark.bm 10 do |r| 24 r.report "アクション" do 25 i.find_element(:xpath => ".//th[contains(text(),'ポイント利用(')]/following-sibling::td").text().gsub(/[円]/,"").gsub(/[,]/,"").to_i 26 end 27 end 28 end 29

ベンチマーク結果

Ruby

1 user system total real 2条件分岐 0.040000 0.020000 0.060000 ( 26.678073) 3 user system total real 4条件分岐 0.050000 0.030000 0.080000 ( 25.499489) 5 user system total real 6条件分岐 0.010000 0.000000 0.010000 ( 0.017920) 7 user system total real 8条件分岐 0.050000 0.030000 0.080000 ( 24.706779) 9 user system total real 10条件分岐 0.000000 0.000000 0.000000 ( 0.011791) 11 user system total real 12条件分岐 0.000000 0.000000 0.000000 ( 0.012549) 13 user system total real 14条件分岐 0.050000 0.020000 0.070000 ( 25.037440) 15 user system total real 16条件分岐 0.050000 0.020000 0.070000 ( 26.076752) 17 user system total real 18条件分岐 0.000000 0.000000 0.000000 ( 0.018694) 19 user system total real 20アクション 0.000000 0.000000 0.000000 ( 0.038412) 21 user system total real 22アクション 0.010000 0.000000 0.010000 ( 0.028833) 23 user system total real 24アクション 0.010000 0.000000 0.010000 ( 0.032794) 25 user system total real 26アクション 0.000000 0.000000 0.000000 ( 0.026723) 27 user system total real 28アクション 0.000000 0.000000 0.000000 ( 0.030527) 29 30

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

rescueの使用方法は誤りではありませんが、何かしらのループ内で例外処理を行うのは処理が遅くなる要因になります。

参考: rubyの例外処理が遅かった?はなし - Qiita

とは言えSelenium側で例外を投げるようにしているため、例外処理をしなければなりません。
Rubyバインディングのソースコードを斜め読みしてみましたが、恐らくこの辺りで書かれている通り、先にSelenium::WebDriver::Error::TimeOutErrorが発生して、メソッド内で該当エラーが投げられたらSelenium::WebDriver::Error::NoSuchElementErrorを投げ直すようにしているようです。

SeleniumのWikiに書いていないか見てみましたら、タイムアウトに関する項目がありました。
このページの説明によるとやはりfind_elementメソッドはNoSuchElementErrorを投げる前に指定時間待機するようなので、待機時間を明示することで対応するのが良さそうです。

全体のタイムアウトを設定するには以下のように行うようです。

ruby

1driver = Selenium::WebDriver.for :firefox 2driver.manage.timeouts.implicit_wait = 3 # seconds

一部(今回で言えばpointメソッドのみ)のタイムアウト時間を変えるには以下のようにするようです。

ruby

1wait = Selenium::WebDriver::Wait.new(:timeout => 3) 2wait.until { driver.find_element(:id => "cheese").displayed? }

投稿2016/11/04 04:27

from_kyushu

総合スコア17

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki_90453

2016/11/06 14:23

ご返事が遅くなってしまい申し訳ございません。 先程、私の環境で試してみましたが実行時間はあまり変わらず、あいかわらずとても時間が掛かってしまいます。 rescueを使わず、エラーで動作が止まらなくさせる方法はないのでしょうか? def point(i) if element_present?(i, :xpath, ".//table[@class='shipInfo']//th[contains(text(),'ポイント利用')]") i.find_element(:xpath => ".//th[contains(text(),'ポイント利用')]/following-sibling::td").text() else return "なし" end end def element_present?(where, how, what) wait = Selenium::WebDriver::Wait.new(:timeout => 1) # $selenium.manage.timeouts.implicit_wait = 0 wait.until{ where.find_element(how, what)} true # rescue Selenium::WebDriver::Error::NoSuchElementError rescue Selenium::WebDriver::Error::TimeOutError false end
yuki_90453

2016/11/06 14:25

このpointメソッドに引数は既にfind_elementで指定したセレクターが入ります。 ですのでpointメソッド内で使われているxpathはメソッドチェーンのような状態になっているのですが、それが原因で遅いのでしょうか?
from_kyushu

2016/11/07 01:40 編集

> メソッドチェーンのような状態になっているのですが、それが原因で遅いのでしょうか? あまり関係がないと思われます。 こちらでベンチマークを取ってみたのですが、コメントではコードのハイライトが効かないようなので別回答として投稿します。
guest

0

ベストアンサー

提示のコードだけではこちらで試せないので、以下のようなベンチマークを実行してみましたが実行時間は変わらないですね...。
10回程度実行してみましたが、全てに置いてSelenium::WebDriver::Waitを使ったほうが若干遅いという結果になりました。
systemtotalの欄がWait非使用と比べてWait使用のほうが短いのは、恐らくfind_elementの実行結果がキャッシュされているためだと予想しています。

ruby

1require "selenium-webdriver" 2require "benchmark" 3 4def accepting_label elem, timeout = 0 5 wait = Selenium::WebDriver::Wait.new timeout: timeout 6 wait.until{ 7 elem.find_element :xpath, "./div[@class='boxItemData']/p[@class='entry-stateLavel is-accepting']" 8 }.text() 9rescue Selenium::WebDriver::Error::TimeOutError 10 "なし" 11end 12 13def accepting_label_notimeout elem 14 elem.find_element(:xpath, "./div[@class='boxItemData']/p[@class='entry-stateLavel is-accepting']").text() 15rescue Selenium::WebDriver::Error::NoSuchElementError 16 "なし" 17end 18 19def label_notimeout elem 20 elems = elem.find_elements(:xpath, "./div[@class='boxItemData']/p[@class='entry-stateLavel is-accepting']") 21 elems.empty? ? "なし" : elems[0].text() 22end 23 24driver = Selenium::WebDriver.for :chrome 25driver.get "https://teratail.com/tags/Ruby" 26 27elements = driver.find_elements(:xpath, "//div[@class='boxContentWrap btnAttention']/ul/li") 28 29Benchmark.bm(10) do |bm| 30 bm.report "no timeout" do 31 elements.each{|line| accepting_label_notimeout line} 32 end 33 34 bm.report "timeout = 0" do 35 elements.each{|line| accepting_label line, 0} 36 end 37 38 bm.report "no rescue" do 39 elements.each{|line| label_notimeout line} 40 end 41end 42 43driver.quit
user system total real no timeout 0.000000 0.047000 0.047000 ( 28.840176) timeout = 0 0.000000 0.015000 0.015000 ( 31.142968) no rescue 0.000000 0.000000 0.000000 ( 28.656518)

こちらはteratailのRubyタグが付いた注目の質問からラベルが「受付中」になっているものを探すようにしてます。
「解決済」の場合はクラス名がis-resolvedのためTimeOutError(またはNoSuchElementError)が発生するようになっています。
ループ回数はそれぞれ20回程度です。
また、参考程度にfind_elementsメソッドが要素が見つからなければ空配列を返す仕様を利用してrescueを使用しないメソッドも作って実行してみましたが、ループ回数が20回程度であればrescueの有無は実行時間にさほど影響を与えないようです。

そもそも、質問者様の環境で起こっている速度低下の原因は本当に提示されたコードの部分なのでしょうか?
私は質問者様が提示されたコード以上の情報を知ることが出来ないため上記のようなベンチマークをでっち上げてみましたが、こちらで試した限りrescueの有無はさほど影響しないと判断せざるを得ないようです。
他にボトルネックになっている箇所(ページ読み込み速度がそもそも遅くなっている、前段階の処理に時間がかかっている等)がないか、一度切り分けを行ってみたほうがよいのではないかと思います。

投稿2016/11/07 01:41

from_kyushu

総合スコア17

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki_90453

2016/11/08 07:13

詳しいご回答ありがとうございます。 こちらもBenchmarkを使いボトルネックらしい部分でベンチマークを行って見ました。 結果、条件分岐の部分が時間掛かっているように思えます。(コード及び、結果は質問下部に追記しております。) そのボトルネックの部分ですがuser,system,totalの部分は数値が低いのですが、realの部分だけ平均20秒近く掛かっております。 このreal部分は何か?調べてみたのですが、「CPU がぼーっとしていたり,他の仕事をやっている時間も含まれる。」と書かれておりましたが、なぜこの動作のみそのような状態になるのかわかりません。 このreal部分の何もしていない時間に問題があるのではないかと思いますが、どのように対処すればよいかご教授頂けないでしょうか。
yuki_90453

2016/11/08 07:16

最後の部分について回答を忘れていました。 環境としては、他に何もアプリケーションを動作させていない状態で実施しております。 またボトルネックと思われる部分をコメントアウトし実行すると、600秒→50秒程になるので確実かと思います。
guest

0

rescueって、

begin 処理1 rescue 例外クラス rescueの処理 end

だと思っていましたが(対象処理に範囲の限定の為?)
貴殿のソースにはbeginが見当たらない。
見当違いだったらすみません。

投稿2016/11/03 10:24

MasahikoHirata

総合スコア3747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki_90453

2016/11/03 11:45

beginは必須なのでしょうか?動作に支障がないのであえて省略しております。
退会済みユーザー

退会済みユーザー

2016/11/03 11:52

begin があった方が見易い(理解し易い)ので、私は省略しないですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問