Нужно сбрасывать все дерево DOM с идентификатором элемента с сервера селена

Я использую python selenium для тестирования веб-автоматизации. Ключевой частью автоматизации является поиск правильного элемента для видимого пользователем объекта на HTML-странице. Следующий API будет работать большую часть времени, но не все время.

find_element_by_xxx, xxx can be id, name, xpath, tag_name etc. 

Когда HTML-страница слишком сложна, я бы хотел найти дерево dom. Подумайте, можно ли спросить селеновый сервер для сериализации всей DOM (с идентификатором элемента, который можно использовать для выполнения действий через сервер webdriver). Клиентская сторона (скрипт python) может выполнить собственный алгоритм поиска, чтобы найти нужный элемент.

Обратите внимание, что python selenium может получить всю html-страницу

 drv.page_source 

Однако разбор этого не дает идентификатора внутреннего элемента с точки зрения селенового сервера, следовательно, не является полезным.

EDIT1: перефразируйте его, чтобы сделать его более понятным (спасибо @alecxe): здесь требуется сериализованное представление всех элементов DOM (с сохраненной структурой DOM) на сервере selenium, это сериализованное представление может быть отправлено на клиентскую сторону ( приложение python selenium test), которое может выполнять собственный поиск.

5 Solutions collect form web for “Нужно сбрасывать все дерево DOM с идентификатором элемента с сервера селена”

Пытаться:

 find_elements_by_xpath("//*") 

Это должно соответствовать всем элементам документа.

ОБНОВЛЕНИЕ (для соответствия уточнению вопроса):

Используйте javascript и верните DOM в виде строки:

 execute_script("document.documentElement.outerHTML") 

Проблема

Хорошо, поэтому могут быть случаи, когда вам нужно выполнить некоторую существенную обработку страницы на стороне клиента (Python), а не на стороне сервера (браузера). Например, если у вас есть какая-то система машинного обучения, уже написанная на Python, и она должна анализировать всю страницу перед выполнением действий над ними, то, хотя это возможно сделать с кучей вызовов find_element , это становится очень дорогостоящим, потому что каждый вызов – это кругооборот между клиентом и сервером. И переписать его для работы в браузере может быть слишком дорого.

Почему идентификаторы Selenium не будут делать это

Однако я не вижу эффективного способа получить сериализацию DOM вместе с собственными идентификаторами Selenium. Selenium создает эти идентификаторы по мере необходимости, когда вы вызываете find_element или когда DOM-узлы возвращаются из вызова execute_script (или передаются execute_script вызову, который execute_async_script дает скрипту). Но если вы вызываете find_element для получения идентификаторов для каждого элемента, вы вернетесь к квадрату. Я мог представить себе украшение DOM в браузере необходимой информацией, но нет публичного API, чтобы запросить некоторую предварительную WebElement идентификаторов WebElement . По сути, эти идентификаторы разработаны так, чтобы быть непрозрачными, поэтому даже если решение каким-то образом способствовало получению необходимой информации, я был бы обеспокоен жизнеспособностью кросс-браузера и постоянной поддержкой.

Решение

Однако есть способ получить систему адресации, которая будет работать с обеих сторон: XPath. Идея состоит в том, чтобы проанализировать сериализацию DOM в дереве на стороне клиента, а затем получить XPath интересующих вас узлов и использовать их для получения соответствующего WebElement. Поэтому, если вам нужно будет выполнить десятки клиентских серверных обращений, чтобы определить, какой из них нужно выполнить, вы можете уменьшить это до первоначального запроса источника страницы плюс один вызов find_element с помощью XPath вам нужно.

Вот супер простое доказательство концепции. Он выбирает основное поле ввода на главной странице Google.

 from StringIO import StringIO from selenium import webdriver import lxml.etree # # Make sure that your chromedriver is in your PATH, and use the following line... # driver = webdriver.Chrome() # # ... or, you can put the path inside the call like this: # driver = webdriver.Chrome("/path/to/chromedriver") # parser = lxml.etree.HTMLParser() driver.get("http://google.com") # We get this element only for the sake of illustration, for the tests later. input_from_find = driver.find_element_by_id("gbqfq") input_from_find.send_keys("foo") html = driver.execute_script("return document.documentElement.outerHTML") tree = lxml.etree.parse(StringIO(html), parser) # Find our element in the tree. field = tree.find("//*[@id='gbqfq']") # Get the XPath that will uniquely select it. path = tree.getpath(field) # Use the XPath to get the element from the browser. input_from_xpath = driver.find_element_by_xpath(path) print "Equal?", input_from_xpath == input_from_find # In JavaScript we would not call ``getAttribute`` but Selenium treats # a query on the ``value`` attribute as special, so this works. print "Value:", input_from_xpath.get_attribute("value") driver.quit() 

Заметки:

  1. В приведенном выше коде не используется driver.page_source поскольку в документации Selenium указано, что нет никакой гарантии относительно свежести того, что он возвращает. Это может быть состояние текущей DOM или состояние DOM при первой загрузке страницы.

  2. Это решение страдает от тех же проблем, с которыми find_element страдает от динамического содержимого. Если DOM изменяется во время анализа, то вы работаете над устаревшим представлением DOM.

  3. Если вам нужно генерировать события JavaScript во время анализа, и эти события меняют DOM, вам нужно снова получить DOM. (Это похоже на предыдущую точку, но решение, которое использует вызовы find_element может, вероятно, избежать проблемы, о которой я говорю в этой точке, тщательно упорядочив последовательность вызовов.)

  4. lxml может, возможно, структурно отличаться от дерева DOM таким образом, что XPath, полученный из lxml , не адресует соответствующий элемент в DOM. Какие процессы lxml – это очищенное сериализованное представление, которое браузер имеет для HTML-кода. Поэтому, пока код написан для предотвращения проблем, о которых я упоминал в пунктах 2 и 3 , я не считаю это вероятным сценарием, но это не невозможно.

См. Мой другой ответ на вопрос о любых попытках получить идентификаторы Selenium.

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

Другой метод из моего другого ответа – использовать execute_script для выполнения поиска в браузере, а затем вернуть все необходимые элементы. Например, этот код потребует трех раундов, но может быть сокращен до одного раунда:

 el, parent, text = driver.execute_script(""" var el = document.querySelector(arguments[0]); return [el, el.parentNode, el.textContent]; """, selector) 

Это возвращает элемент, родитель элемента и текстовое содержимое элемента на основе любого селектора CSS, который я хочу передать. В случае, когда страница загружена jQuery, я мог бы использовать jQuery для выполнения поиска. И логика может стать настолько сложной, насколько это необходимо.

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

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

http://selenium-python.readthedocs.org/en/latest/test-design.html?highlight=page%20object

Вы также можете прокручивать все элементы страницы и сохранять их по одному за раз, но там должна быть библиотека, которая может это сделать. Я знаю, что .Net есть htmlAgility. Я не уверен в python.

Обновление Я нашел это … возможно, это поможет вам. Html Agility Pack для python

На самом деле вы можете сделать это довольно легко. Напишите вывод в поток, как var w = window.open... и затем document.write...

рекурсивно перебирать объект документа, возвращающий JSON.Stringify, возвращающий каждый объект. Я предлагаю вам также бросить typeof .

 var s = recurse(obj) { for(var i in obj) { return typeof(i) + ":" + i.toString() + ":" + JSON.stringify(obj[i]); } } 

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

Я нашел этот вопрос в поисках чего-то подобного, но я надеялся на объект DataTable (я использую .Net), который я мог бы связать в какое-то окно отладки, что-то лучше, чем хром. Прежде, чем я использовал firebug, чтобы сделать это, но это сорта мертва.

Таким образом, вы также можете получить эти данные, но в режиме реального времени с помощью отладчика.

  • Установите флажок, используя Selenium с Python
  • Тесты Selenium в Python - где мой файл журнала?
  • обрабатывать окно tinymce с помощью python, selenium и phantomjs
  • Python: Selenium напишите в текстовом поле формы
  • Как запустить веб-драйвер selenium за прокси-сервером, который нуждается в аутентификации в python
  • выбор iframe с использованием python selenium
  • как открыть две вкладки в python splinter
  • Селен не запускает Chromedriver
  • Python - лучший язык программирования в мире.