3

I'm using this code for waiting for an element to load:

browser = webdriver.Chrome(executable_path=chromedriver,options=ChromeOpts, desired_capabilities=captain) 
wait = WebDriverWait(browser, 5)
openbrowser = browser.get(url) 
wait.until(EC.presence_of_element_located((By.ID, 'h1')))
browser.execute_script("window.stop();")

However, what I really need is to wait for one element OR another. So I can, for example, wait for 'h1' OR 'rf-footer'.

thanks,

rafasalo
  • 243
  • 2
  • 16

3 Answers3

3

You can combine the two checks in a single wait() operation - by using a python's lambda expression, using the find_elements_*() methods glued together with an or:

element = wait.until(lambda x: x.find_elements_by_id("id1") or x.find_elements_by_css_selector("#id2"))[0]

You can use this approach even to get the element that is matched first - the [0] at the end.
This solution works, as find_elements_*() returns a list of all matched elements, or an empty one if there aren't such (a boolean false). So if the first call doesn't find anything, the second is evaluated; that repeats until either of them finds a match, or the time runs out.

This approach gives the benefit the wait.until() will stop immediately after one of the two passes - e.g. speed.


Versus a try-catch block - in it, the first condition has to fail (waited for up to X seconds, usually 10), for the second one even to be checked; so if the first is false, while the second true - the runtime/wait is going to be at least X seconds plus whatever time the second check takes.
In the worst case, when both are false, you are in for 2X wait, versus X in combining the two. If you add other conditions, you are only increasing the factor.

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60
  • Thanks!! I've tried the first one and got that: `AttributeError: 'WebDriver' object has no attribute 'presence_of_element_located'` – rafasalo Jan 05 '19 at 11:49
  • Any ideas for solving it? – rafasalo Jan 05 '19 at 12:08
  • My bad, fat-fingered it on mobile (likewise now, fingers crossed :). The issue was `presence_of_element_located()` is a class in `expected_conditions`, not a method of the webdriver object (which is the `x` in the lambda). – Todor Minakov Jan 05 '19 at 13:43
  • Now It's occurring another exception. After I execute: `browser.execute_script("return document.documentElement.outerHTML")` **I get:** `selenium.common.exceptions.WebDriverException: Message: unknown error: Cannot read property 'outerHTML' of null` As I'm trying to get the HTML, it seems it's returning null. – rafasalo Jan 05 '19 at 14:30
  • My code: `browser = webdriver.Chrome(executable_path=chromedriver, options=ChromeOpts, desired_capabilities=capa) wait = WebDriverWait(browser, 10) openbrowser = browser.get(url) #navigate to the page a = wait.until(lambda x: EC.presence_of_element_located((By.ID, "id_max")) or EC.presence_of_element_located((By.ID, "max_id"))) browser.execute_script("window.stop();") html = browser.execute_script("return document.documentElement.outerHTML")` BTW, already changed to innerHTML. I see on the console that before the page stops loading, the console's already finalized to run the code. – rafasalo Jan 05 '19 at 14:36
  • 1
    I think I got it, I placed a `time.sleep(x)` before running the last script. – rafasalo Jan 05 '19 at 14:45
  • 1
    The part with `expected_conditions` is incorrect, because `ec.presence_of_element_located` returns an object and therefore using the `or` operator will always return the part before `or` but the part after `or` will never be executed. – Jakub Bláha May 16 '20 at 12:13
  • 1
    @JakubBláha you are correct, that won't do the job - but for a different reason. The first `presence_of_element_located()` when the element is not there will not return an object, but will raise an exception - which is caught by the `wait.until()`, and the cycle is repeated. Thus the second `presence_of_element_located()` will actually never be executed - that's the part you're correct about. I'll edit the answer, by keeping only the `find_elements*()` - that will always work as if there are no found, they return empty list (boolean false). – Todor Minakov May 16 '20 at 17:50
  • What exception would it raise though? It doesn't actually do anything until it's called by the `until` method, therefore it cannot raise any exception. The second object was never even instantiated. Thanks for editing the answe and thanks for providing an actual working solution. – Jakub Bláha May 17 '20 at 20:56
  • It raises ElementNotFound, that's handled by the `until()`'s catch block. – Todor Minakov May 17 '20 at 21:07
0

You can do this by using expected_conditions

from selenium.webdriver.support import expected_conditions

the full solution can be found here: Selenium Expected Conditions - possible to use 'or'?

Califlower
  • 467
  • 4
  • 15
  • Thanks! I got it buy using TRY/EXCEPT. `openbrowser = browser.get(url) #navigate to the page try: wait.until(EC.presence_of_element_located((By.ID, id_stop))) except: wait.until(EC.presence_of_element_located((By.ID, 'rf-footer'))) browser.execute_script("window.stop();")` – rafasalo Jan 05 '19 at 03:01
-1

I got it buy using TRY/EXCEPT.

openbrowser = browser.get(url) #navigate to the page 
try: 
   wait.until(EC.presence_of_element_located((By.ID, id_stop))) 
except: 
   wait.until(EC.presence_of_element_located((By.ID, 'rf-footer')))
browser.execute_script("window.stop();")
rafasalo
  • 243
  • 2
  • 16