How to get a Selenium/Ruby bot to wait before perf

2020-02-10 06:16发布

I'm building a Selenium/Ruby web bot that clicks on elements. The problem is, sometimes there isn't enough time for the page to load before the bot decides it can't find the element.

What's the Ruby way to get Selenium to wait before performing an action? I would prefer explicit waiting, but I'm fine with implicit waiting too.


I tried to use the wait.until method:

require "selenium-webdriver"
require "nokogiri"
driver = Selenium::WebDriver.for :chrome
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
driver.navigate.to "http://google.com"
driver.wait.until.find_element(:class, "gb_P").click

But I'm getting the following error:

Undefined method 'wait' for <Selenium::WebDriver>

I also tried:

require "watir-webdriver/wait"
...
driver.find_element(:class, "gb_P").wait_until.click

but that's also giving me an undefined method error:

undefined method `when_present' for #<Selenium::WebDriver...>

7条回答
爱情/是我丢掉的垃圾
2楼-- · 2020-02-10 06:59

Okay. This answer has been asked many times in many different contexts. So I just want to answer it here once and for all.

There are three ways this can be done. And each is useful in certain contexts.

First, you can use an EXPLICIT wait. This has nothing to do with whether the page loads or not. It simply tells the script to wait. In other words, if your page loads in 11 seconds and your explicit wait is 10 seconds, the clickable element still won't be available. You can work around this inefficiency by using an expected condition. See, e.g., the Selenium manpages:

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"

wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
begin
  element = wait.until { driver.find_element(:class => "gb_P") }
ensure
  driver.quit
end

^ This will wait either: 10 seconds OR until the element is found.

Second, you can use an implicit wait. This is very similar to the explicit wait with expected condition. However, where the explicit waits applies to the element being queried, implicit wait applies to the WebDriver Object. In other words, if your script only uses a single webdriver, it will wait either: the implicit wait time for EVERY element OR until each element is found (until failure). For example:

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :firefox
driver.manage.timeouts.implicit_wait = 10 # seconds

driver.get "http://google.com"
element = driver.find_element(:class => "gb_P")

Third, you can call a Javascript function to click the page. The advantage of this is that once the page's Javascript loads, the item will be clickable and you don't have to actually wait for the page to render. A lot of time when you're "waiting for the page" you're actually waiting on the client side for the rendering engine to construct the page. You can bypass that process by simply clicking the underlying element before the page is actually rendered.

The drawback of this is that it doesn't mirror an actual human clicking the page. For example, if the button you want to click is hidden by a popup. Selenium won't allow you to click it, but a JS function will.

You can use this method by doing:

click = driver.execute_script("document.getElementsByClassName('gb_P')[0].click();")
查看更多
走好不送
3楼-- · 2020-02-10 07:00

Have you tried the when present, to wait for the button (You could also make it wait for a certain div to )

require "watir-webdriver/wait"
driver.button(:class => 'gb_P').when_present.click

As for explicit waiting

sleep *seconds*

or a better way as to not waste time

sleep *seconds* until driver.element(:id/class/etc, 'value').exists?
查看更多
爷的心禁止访问
4楼-- · 2020-02-10 07:01

Just use

sleep(number_of_seconds)

Then you can wait for it.

查看更多
够拽才男人
5楼-- · 2020-02-10 07:02

A potential answer is in your stacktrace.

driver.element(:class, "gb_P").when_present.click
查看更多
劫难
6楼-- · 2020-02-10 07:02

Using Webdriver RC and C# I find this is the most effective way to make the driver wait:

Task.Delay(1000).Wait();
查看更多
Emotional °昔
7楼-- · 2020-02-10 07:09

You are using wait as WebDriver function, but it isn't. Try this

element = wait.until { driver.find_element(:class => "gb_P") }
element.click
查看更多
登录 后发表回答