Как перемещаться по вкладкам при помощи python и seleniumWD?

Да sleep это зло, согласен )

Тут более подробно:

1 лайк

В общем делаю так

s = self.driver.window_handles
print(s)
b = self.driver.current_window_handle
print(b)
time.sleep(10)
self.driver.switch_to.window(b)

Но меня не перекидывает на первую вкладку(

А где код выбора нужной вкладки?

Смотри код, который arturk писал, как вариант:

b = self.driver.current_window_handle

for handle in driver.window_handles:
    if  driver.switch_to.window(handle).current_window_handle != b
       .....
      driver.close()

self.driver.switch_to.window(b)

вот еще вариант:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

#....
#....

wait = WebDriverWait(driver, 10)

current_window = driver.current_window_handle
old_windows = driver.window_handles

#open new window here...
#.....

wait.until(ec.new_window_is_opened(old_windows))
new_window = [i for i in driver.window_handles if i not in old_windows]
driver.switch_to.window(new_window[0])

#do something in new window
#....

driver.close()
driver.switch_to.window(current_window)
2 лайка

Я не совсем понял зачем мне выбирать нужную вкладку. Вот как только появились новые вкладки и мой фокус сменился на новую вкладку я тут же хочу перейти на первую, а новые можно и не трогать, а можно и закрыть.

Спасибо за несколько примеров, буду пробовать сейчас.

Фокус сменился у браузера, а у селениума нет. надо явно сказать куда переключить и что закрыть.

Если делаю так

current_window = self.driver.current_window_handle
for handle in self.driver.window_handles:
        if self.driver.switch_to.window(handle) != current_window:
            self.driver.close()

То замечаю в самом конце, что закрывается первая вкладка - именно так, что мне нужна.

Вот так попробовал

from selenium.webdriver.common.keys import Keys
self.driver.find_element_by_tag_name("body").send_keys(Keys.ALT + Keys.NUMPAD1)
self.driver.switch_to.window(current_window)

Не сработало. Просто не реагирует и все(

Немного непонятно чего именно вы добиваетесь этим кодом )

Из всех команд, работает только закрытие. Хоть фокус и находится на вкладке с pdf документом, но при вызове команды

self.driver.close()

Закрывается именно 1 вкладка (которая нужна), хотя она даже не в фокусе.

У меня есть подозрение, что проблема из-за страниц с pdf. Они может какие-то другие.

Значит так… для селениума нет понятия “вкладка” это в браузере она может выглядеть как вкладка, но селениум видит это как отдельное окно. Посему, когда открывается браузер в первый раз, то селениум начинает работать с ним как с первым окном. Потом, если каким-то образом, вручную или по нажатию на кнопки, открываются другие “вкладки”, то в браузере это выгладит как смена контекста, фокуса или чего угодно еще, но селениум по прежнему видит то, самое первое, окно. Соответственно все команды отправляются именно в него, в том числе и close(). Что бы переключится в другие окна, нам надо узнать как их зовут. Для этого используют driver.window_handles - эта команда вернет список имен всех открытых окон (“вкладок”) включая и нашу текущую. Соответственно, что бы переключится в другое окно, нам надо из этого списка выбрать имя, которое отличается от имени текущего окна, а текущее окно можно узнать по driver.current_window_handle. Важно понимать, что окна (вкладки) открываются не мгновенно, потому перед тем, как искать имена открытых окон, надо подождать. Подождали, нашли новое окно, переключились, закрыли, переключились обратно. Селениум не переключается между окнами сам, это задача программиста.

3 лайка

Спасибо. Уж разжевали дальше некуда. Получается, что я запоминаю свою вкладку, потом нажимаю кнопку и у меня открывается еще 2 вкладки, но фокус остается на первой (именно поэтому close закрывает именно первую вкладку, хотя я вижу содержимое третьей). затем я получаю имена всех вкладок/окон. переключаюсь например на третью (которую вижу сейчас) и закрываю ее, то же самое делаю и со второй вкладкой и в итоге остаюсь на своей первоначальной и фокус перевожу тоже на нее.

Отсюда вопрос - как мне переключаться по вкладкам, если хэндлы каждый раз называются по новому?

Не думаю что хендлы меняются. Они постоянные для каждого открытого окна. Хотя в ОС хендл - это обычно целое число, а вот вебдрайвер их как-то по своему формирует и они больше на guid какого-то com+ обьекта похожи.
Вобщем, выколите себе глаза, или завяжите… да, наверное так лучше будет. Забудьте что вы видите. Ваш драйвер не видит ничего, он просто делает.

  1. Вы запускаете тест.
  2. Регистрируете драйвер.
  3. Говорите драйверу инициализировать браузер.
  4. Драйвер дает команду ОС запустить браузер.
  5. ОС выполняет запуск браузера, инициализацию процесса, присвоение ПОЖИЗНЕННОГО хендла процессу, инициализацию окна, присвоение ПОЖИЗНЕННОГО хендла окну (driver.current_window_handle), прорисовку окна, запуск цикла событий, распарареливание потоков в процессе, присвоение ПОЖИЗНЕННЫХ хендлов потокам и т.д. А ваш драйвер в этот момент уже выполняет следующую задачу, если вы ему явно не указали подождать некоторое время. И вот если процесс браузера ещё не зарегестрирован в ОС, драйвер его не увидит, по-этому надо ждать.
  6. Вы делаете штуки с драйвером, драйвер делает штуки с браузером.
  7. Сайт в браузере решает открыть ещё одно окно или закладку браузера. И дает комманду браузеру(не драйверу). Драйвер не знает, что открылось ещё одно окно. Но он может об этом узнать, если спросит у родительского процесса, сколько у него дочерних процессов запущено (self.driver.window_handles)
  8. Вы запоминаете текущее окно, на котором сфокусирован драйвер (current_window = self.driver.current_window_handle)
  9. Переключаетесь на другое окно (driver.switch_to.window(handle)). Если такого хендла нету, то вы останетесь в прежнем (первом) окне.
  10. Делаете что вам надо с другим окном.
  11. Если надо вернуться к первому окну - self.driver.switch_to.window(current_window)
  12. Делаете что вам надо с первым окном.

Хэндлы не меняются. Или это не хендлы. Они постоянные на время жизни обьекта. Когда окно, процесс, обьект закрываются (уничтожаются), хендл затирается. Если создастся новое окно, у него уже будет другой хендл.

PS: Хендл вебдрайвера для окон выглядит как compound кизяк из 90-х годов, че нельзя просто было обычный ОС хендл использовать, нет надо какого-то своего гов**а намутить и главное ниодно руководство не описывает, как они формируют свой хендл ИД и кроме меня этим вопросом по ходу задался только программист который писал эту часть вебдрайвера.

3 лайка

Спасибо вам большое. Я понял всю логику работы. Я понял как это работает и при помощи каких инструментов.

Мне это нужно было, чтоб я просто видел что делает selemium в окне. но в дальнейшем тесты будут запускаться на гриде. Поэтому я забил. Раз фокус находится в том окне где мне нужно, хотя и вижу я другое окно, то пусть так и будет. Там selenium продолжает делать какие-то действия.

Но перемещение по вкладкам у меня так и не получилось сделать.

Нашел такой пример

import unittest
import time
from selenium import webdriver
import selenium.webdriver.support.ui as ui
from selenium.webdriver.common.keys import Keys



class SwitchHandleTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(30)
        self.driver.set_page_load_timeout(30)

    def autister_login_test(self, ):
        time.sleep(3)
        self.driver.get('http:/reddit.com')
        time.sleep(3)
        window_before = self.driver.window_handles[0]
        time.sleep(3)
        self.driver.find_element_by_tag_name('body').send_keys(Keys.CONTROL + 't')
        time.sleep(3)
        window_after = self.driver.window_handles[1]
        time.sleep(3)
        self.driver.switch_to.window(window_after)
        time.sleep(3)
        self.driver.get('http://bing.com')


    def tearDown(self):
        self.driver.quit()

Но оказалось, что sendkeys в хромдрайвере не работает.

Немного поменял и все получилось. В зависимости от того, что мы ставим в switch_to.windows(after или before) мы перемещаем фокус либо на 1 вкладку, либо во вторую.

import unittest
import time
from selenium import webdriver
import selenium.webdriver.support.ui as ui
from selenium.webdriver.common.keys import Keys



class SwitchHandleTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(30)
        self.driver.set_page_load_timeout(30)

    def autister_login_test(self, ):
        self.driver.get('http:/reddit.com')
        time.sleep(3)
        window_before = self.driver.window_handles[0]
        self.driver.execute_script("window.open('http://youtube.com/');")
        time.sleep(3)
        window_after = self.driver.window_handles[1]
        self.driver.switch_to.window(window_after)
        self.driver.get('http://bing.com')
        time.sleep(3)
        self.driver.close()



    def tearDown(self):
        self.driver.quit()

Спасибо всем .что помогли. Задачу решил. Потратил на это 2 дня. Ужасное решение, но самое главное, что работает.

Смоделировал ситуацию, как у меня. 3 вкладки, мне нужно переместиться на первую и продолжить в ней работу, а остальные можно закрыть.

Вот код.

import unittest
import time
from selenium import webdriver
from selenium.webdriver.common.by import By



class SwitchHandleTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(30)
        self.driver.set_page_load_timeout(30)

    def autister_login_test(self, ):
        self.driver.get('http:/ya.ru',)
        window_before = self.driver.window_handles[0]
        self.driver.execute_script("window.open('http://youtube.com/');")
        self.driver.execute_script("window.open('http://bing.com/');")
        window_after = self.driver.window_handles[1]
        self.driver.switch_to.window(window_after)
        time.sleep(3)
        self.driver.close()
        window_after1 = self.driver.window_handles[1]
        self.driver.switch_to.window(window_after1)
        self.driver.close()
        self.driver.switch_to.window(window_before)
        self.driver.find_element(By.XPATH, "//input[@id='text']").send_keys('Какой же позорный костыль')
        self.driver.find_element(By.XPATH, "//button[@type='submit']").click()
        time.sleep(3)

    def tearDown(self):
        self.driver.quit()

Все же закрыть другие окна нужно, так как есть определенные действия на странице, которые могут не сработать, если окно не в фокусе, например всяческие обработчики, навешанные на события перерисовки, браузер просто не будет их взвывать, если окно не в фокусе.

1 лайк

Такое решение будет работать в большинстве случаев, но не всегда. Так как список окон, который возвращает window_handles это неупорядоченный список (set), и в нем совсем не гарантируется то, что новые окна будут отсортированы в порядке появления и иметь соответствующие индексы, особенно в ситуациях, когда уже есть открытые окна. Но такие ситуации к счастью довольно редки.

1 лайк

А это надо заменить на явное ожидание:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

wait = WebDriverWait(driver, 10)
wait.until(ec.new_window_is_opened(old_windows))
#или
wait.until(ec.number_of_windows_to_be(3)) # 2,1 etc.
1 лайк