XML имеет древовидную структуру. В документе всегда имеется корневой элемент (инструкция к дереву отношения не имеет). У элемента дерева всегда существуют потомки и предки, кроме корневого элемента, у которого предков нет, а также тупиковых элементов (листьев дерева), у которых нет потомков. Каждый элемент дерева находится на определенном уровне вложенности (далее - «уровень»). У элементов на одном уровне бывают предыдущие и следующие элементы.
Это очень похоже на организацию каталогов в файловой системе, и строки XPath, фактически, - пути к «файлам» - элементам.
Например, рассмотрим XHTML документ:
<html > <body > <div > Первый слой <span > блок текста в первом слое</ span > </ div > <div > Второй слой</ div > <div > Третий слой <span class = "text" > первый блок в третьем слое</ span > <span class = "text" > второй блок в третьем слое</ span > <span > третий блок в третьем слое</ span > </ div > <img / > </ body > </ html >
XPath-путь /html/body/*/span[@class] (полный синтаксис имеет вид /child::html/child::body/child::*/child::span ) будет соответствовать в нём двум элементам исходного документа - первый блок в третьем слое и второй блок в третьем слое .
Путь делится на шаги адресации, которые разделяются символом «косая черта» / . Каждый шаг адресации состоит из трех частей:
Анализ ведется слева направо. Если первый символ это / , то путь адресации считается абсолютным (то есть от корня документа). При этом за узел контекста на первом шаге берется корневой элемент (html). Контекст - это некая точка отсчета, относительно которой рассчитывается следующий шаг адресации. Поэтому на каждом шаге адресации мы получаем новый набор узлов документа, и этот набор становится контекстом для следующего шага адресации.
На втором шаге адресации (child::body) контекстом становится html элемент. Ось child:: говорит о том, что необходимо найти все непосредственные потомки элемента html, а условие проверки body говорит о том, что в формируемый набор элементов нужно включить все узлы с именем body. В ходе второго шага адресации получаем набор узлов, состоящий всего из одного элемента body, который и становится элементом контекста для третьего шага.
Третий шаг адресации: child::* . Ось child:: собирает все непосредственные потомки элемента body, а условие проверки * говорит о том, что в формируемый набор нужно включить элементы основного типа с любым именем. В ходе этого шага получаем набор узлов, состоящий из трех элементов div и одного элемента img.
Четвёртый шаг адресации: child::span . Теперь контекстом является набор из четырёх элементов. И следующий набор узлов создается в четыре прохода (за четыре итерации). При первой итерации узлом контекста становится первый div. Согласно заданной оси child:: и правилу проверки span, в набор включаются непосредственные потомки div-а, имя которых равно span. При второй итерации в набор ничего добавлено не будет, так как у второго div нет потомков. Третья итерация добавит в набор сразу три элемента span, а четвёртая ничего не добавит, так как у элемента img нет потомков. Итак, в ходе проверки получен набор узлов, состоящий из четырёх элементов span. Это и будет контекстом для последующей обработки.
Следующего шага нет, поэтому будет производиться фильтрация отобранного набора. В этом и состоит отличие предикатов от шагов адресации. На каждом шаге адресации получаем новый набор, отталкиваясь от контекста, полученного на предыдущем шаге. В ходе же обработки предиката новый набор получается из текущего методом фильтрации, когда из набора исключаются узлы, не прошедшие условие проверки. В данном случае ось attribute:: говорит о необходимости проверить, если ли у узлов контекста атрибуты, а условие class требует оставить лишь те узлы, у которых задан атрибут с именем class. Фильтрация происходит за четыре итерации, но в окончательный набор попадают только два элемента span.
Оси - это база языка XPath.
Существуют сокращения для некоторых осей, например:
Дополнением к базе является набор функций, которые делятся на 5 групп:
Стандарты Консорциума Всемирной паутины | |
---|---|
Рекомендации |
Canonical XML CDF CSS DOM Geolocation API HTML ITS MathML OWL P3P PLS RDF (Schema) SISR SKOS SMIL SOAP SRGS SSML SVG SPARQL Timed Text VoiceXML WSDL XForms XHTML XHTML+RDFa XInclude XLink XML (Base Encryption Events Information Set namespace Schema Signature) XPath / 1.0 / 2.0 XPointer XProc XQuery XSL XSL-FO XSLT (элементы) XUP |
Примечания |
XAdES XHTML+SMIL |
Рабочие проекты | |
Guidelines |
Web Content Accessibility Guidelines |
Initiative |
Multimodal Interaction Activity Markup Validation Service Web Accessibility Initiative |
Продолжение перевода неофициальной документации Selenium для Python.
Перевод сделан с разрешения автора Baiju Muthukadan.
Оригинал можно найти .
1. Установка
2. Первые Шаги
3. Навигация
4. Поиск Элементов
5. Ожидания
6. Объекты Страницы
7. WebDriver API
8. Приложение: Часто Задаваемые Вопросы
Помимо общедоступных (public) методов, перечисленных выше, существует два приватных (private) метода, которые при знании указателей объектов страницы могут быть очень полезны: find_element and find_elements.
Пример использования:
From selenium.webdriver.common.by import By
driver.find_element(By.XPATH, "//button")
driver.find_elements(By.XPATH, "//button")
Для класса By доступны следующие атрибуты:
ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
Login_form = driver.find_element_by_id("loginForm")
Для примера, рассмотрим следующий исходный код страницы:
Username = driver.find_element_by_name("username")
password = driver.find_element_by_name("password")
Следующий код получит кнопку “Login”, находящуюся перед кнопкой “Clear”:
Continue = driver.find_element_by_name("continue")
Одно из веских оснований использовать XPath заключено в наличии ситуаций, когда вы не можете похвастать пригодными в качестве указателей атрибутами, такими как id или name, для элемента, который вы хотите получить. Вы можете использовать XPath для поиска элемента как по абсолютному пути (не рекомендуется), так и по относительному (для элементов с заданными id или name). XPath указатели в том числе могут быть использованы для определения элементов с помощью атрибутов отличных от id и name.
Абсолютный путь XPath содержит в себе все узлы дерева от корня (html) до необходимого элемента, и, как следствие, подвержен ошибкам в результате малейших корректировок исходного кода страницы. Если найти ближайщий элемент с атрибутами id или name (в идеале один из элементов-родителей), можно определить искомый элемент, используя связь «родитель-подчиненный». Эти связи будут куда стабильнее и сделают ваши тесты устойчивыми к изменениям в исходном коде страницы.
Для примера, рассмотрим следующий исходный код страницы:
Login_form = driver.find_element_by_xpath("/html/body/form") login_form = driver.find_element_by_xpath("//form") login_form = driver.find_element_by_xpath("//form[@id="loginForm"]")
Username = driver.find_element_by_xpath("//form") username = driver.find_element_by_xpath("//form[@id="loginForm"]/input") username = driver.find_element_by_xpath("//input[@name="username"]")
Clear_button = driver.find_element_by_xpath("//input[@name="continue"][@type="button"]") clear_button = driver.find_element_by_xpath("//form[@id="loginForm"]/input")
Для примера, рассмотрим следующий исходный код страницы:
Are you sure you want to do this?
Continue CancelContinue_link = driver.find_element_by_link_text("Continue") continue_link = driver.find_element_by_partial_link_text("Conti")
Для примера, рассмотрим следующий исходный код страницы:
Site content goes here.
Heading1 = driver.find_element_by_tag_name("h1")
Для примера, рассмотрим следующий исходный код страницы:
Site content goes here.
Content = driver.find_element_by_class_name("content")
Для примера, рассмотрим следующий исходный код страницы:
Site content goes here.
Content = driver.find_element_by_css_selector("p.content")
На Sauce Labs есть
я решил озадачиться статьёй про XPath. Зачем мне нужен XPath? У меня есть задача организовать препроцессинг данных: Есть, например, набор операций, описываемых XML документом. Каждая операция — это либо веб-запрос либо запрос к базе данных. Мне необходимо поля из предшествующих операций, подставлять в последующие операции… Примерно так.
Попробую выразиться иначе. XPath — это язык запросов к XML, позволяющий выбирать из XML подмножества данных: как отдельные значения тегов и атрибутов, так и целые наборы значений. XPath запрос + соотвествующий API превращают любой XML документ в подобие базы данных, из которой можно делать выборки.
В моём случае, мне нужно было выбирать из XML некоторые поля, чтобы формировать другие XML документы, содержащие указанные поля. Конечно такая задача может быть решена и без XPath. Но тогда логика будет зашита в код программы. А XPath позволяет логику поиска внутри XML документов вынести во внешние конфигурационные данные. Т.е. для моей программы входными данными являются и XML документы и XPath выражения. В таком сочетании и проявляется мощь XPath.
P.S. Впоследствии, в дополнение к теоретической части, я написал пару постов с описанием практического применения XPath на Java:
Это предисловие. Приступаем к изучению. От добра-добра не ищут. На предмет XML-технологий пока самый интересный сайт: w3schools.org. Поэтому просто перевожу XPath tutorial оттуда.
XPath используется для навигации по элементам и атрибутам XML документа. XPath занимает главенствующее место среди W3C XSLT стандартов. XQuery и XPointer — оба базируются на XPath expressions.
Что есть XPath?
XPath Path Expressions
XPath использует path expressions для выбора узлов или наборов узлов в XML документе. Path expressions сильно напоминают пути, которые вы используете при работе с традиционными компьютерными файловыми системами.
XPath Standard Functions
XPath включает более 100 встроенных функций. Эти функции для строковых значений, цифровых значений, сравнения времени и дат, манипуляции узлами и QName manipulation, манипуляции последовательностью, работы с boolean значениями и другие.
Само собой разумеется и во всех книгах проговаривают, что для нахождения элемента лучше всего и быстрее использовать локаторы id и name и, что характерно, основные примеры по использованию локаторов и по работе Selenium показывают именно с ними. Но в реальной жизни часто бывает так, что id элементов формируется динамически, а потому все привязки к нему бесполезны, class может иметь десятки представителей на странице, а name может отсутствовать. Как вы уже догадываетесь в этом случае нужно применять локаторы xpath и css. В данной статье я не собираюсь говорить о каком то превосходстве над css или сравнивать быстродействие, я лишь расскажу почему я использую именно xpath и как это нужно делать. Букв будет много, так как в свое время мне пришлось достаточно порыться в интернет, чтобы получить нужную мне информацию, я выкладываю все самое полезное, в надежде, что кому это поможет в использовании xpath-локаторов. Важно, что у тебя, мой читатель должно быть хоть небольшой представление о xpath, если его нет, то можешь .
Сначала о том, почему новички (и не только) не любят xpath:
А теперь о том, как обстоят дела на самом деле и в чем преимущества xpath, если его правильно использовать:
— он не уступает (или незначительно уступает) в скорости css
— он понятен и легко читаем, по нему можно понять о каком элементе идет речь
— он похож на язык программирования и его удобно использовать
— можно добраться до самых запрятанных элементов страницы, благодаря выстроенным цепочкам отношений
Итак, несколько правил использования xpath :
Переходим к делу и практике, тот xpath, что указан выше (//header/div/ul/li/a ) на самом деле можно указать в виде //a . Согласись, что есть разница и в длине текста и в понимании его, ведь тут видно по тегу, что это ссылка и ее текст –Pricing. То есть ты можешь и сам найти этот элемент на странице визуально и в случае исключения с таким локатором сразу знаешь, что и где искать!
Теперь о тех командах, которые тебе реально пригодятся для написания грамотных и удобных локаторов:
Как видим id явно сгенерирован и привязаться к нему нельзя, класс тоже не внушает доверия, кроме того Selenium не разрешает использовать сложносоставные имена в локаторе className, но тут есть текст, который решает проблему: // a
Кроме того, очень полезная возможность – это искать элемент по одному из слов в названии класса.Пример:
Все решается вот так: //div , то есть мы ищем элемент, у которого в классе есть какое-то уникальное сочетание слов. Данная возможность contains очень помогает в самых разных ситуациях! Обрати внимание, что параметр и искомое идут через запятую, нельзя писать contains(text()=’smth’)
Теперь пойдут команды отношения элементов (предок, родитель, ребенок, потомок, сестринский элемент), которые позволяют очень гибко найти практически любой элемент на странице при грамотном применении.
Формат использования //начальный элемент/отношение::тег(фильтр) конечного элемента. Обрати внимание на два двоеточия после отношения и не забывай после двоеточий указать тег, а лучше и фильтр искомого элемента, так как потомков может быть и много, а нам нужен какой-то конкретный.
Нам нужно ввести текст в input, но как видишь тут имеется ряд проблем – id динамический, классов и сгенеренных id со словом input на странице много, привязаться вроде не к чему. Но тут есть элемент с текстом, который уникален для страницы, вот к нему и прицепимся:
// div[ text()=’Тема’]/ preceding- sibling:: input — мы сначала находим уникальный элемент с текстом, а потом от него ищем предшествующий сестринский элемент, делая фильтр-уточнение, что ищем именно input. Еще пример:
Нам нужно кликнуть кнопку, на которой нет текста, только иконка, но как видишь у нее все те же проблемы с id плюс есть куча одноименных классов. Нас спасает то, что у предшествующего элемента есть уникальное название класса, вот от него и будем плясать: //div/following-sibling::div – находим элемент у которого есть уникальное слово в названии класса и от него уже ищем следующий сестринский элемент, с тегом div. Учитывай, что даже если сестринских последующих элементов с тегом div будет много вернется только самый первый!
То представим, что нам нужен непосредственно элемент с id=__vz4019, для всех на данной картинке он является родителем (parent) и поэтому его можно вытянуть через любой из них, например // div[ text()=’Тема’]/ parent:: div
Кстати, обращение к родительскому элементу, можно заменить двумя точками и без тега, вот так // div[ text()=’Тема’]/ ..
Так как все элементы в примере — дети, то можно любого из них найти от родителя вот так:
//div/child::input – находим родителя, а от него ищем ребенка с тегом input.
Нам нужна папка именно с определенным именем, но верстка организована так, что сам текст не содержится именно в элементе класса папка, поэтому нам надо найти сначала класс, а потом отфильтровать ту, у которой в потомках есть нужный текст:
// div[@ class=’ listitem Folder’]/ descendant:: span[ text()=’ Folder name’] – сначала находим класс папки, потом среди его потомков ищем тег span и нужный нам текст. Вы можете спросить –а почему просто по тексту не искать? Дело в том, что элементов с таким текстом на странице может быть больше одного, а нам нужна именно папка.
Кстати вместо descendant можно использовать двойной слеш // это означает -любой вложенный элемент. Пример выше превращается в
// div[@ class=’ listitem Folder’]/ / span[ text()=’ Folder name’]
Важно понимать, что можно, но крайне нежелательно использовать в одном локаторе несколько отношений, например:
// div[@ class=’ One]/ child :: div[@ class=’ Two’]/ descendant :: input[@ class=’ Three] . Такой локатор работать будет, но он уже сложно читается и скорее всего есть возможность подобрать другой, не нужно использовать такое без необходимости, помним правило номер 2. Совсем недопустимо использовать в одном локаторе обратные отношения то есть сначала искать потомка, потом его предка или наоборот.
Это все команды и отношения, которые вам пригодятся при написании локаторов! Да, есть еще и другие, вы можете с ними ознакомиться в мануале, прикрепленном в начале статьи, однако я их практически не использовал, а указанных в статье хватает мне и по сей день.
Итак, применяйте указанные команды, ищите правильные элементы, соблюдайте вышеозначенные правила и у вас не будет проблем с написанием грамотных локаторов, которые легко понять, прочесть, исправить. А главное вы поймете, что xpath очень удобен для написания локаторов к любым элементам.
XPath является одним из ключевых моментов на дороге к пониманию XSLT. И на первых порах хочется получить какой-нибудь простой способ поэкспериментировать с ним, чтобы в деталях разобраться, как он работает. Способа такого, впрочем, не наблюдается. Приходится или качать совсем не бесплатные XML/XSLT редакторы, или довольствоваться статичными картинками с zvon.org . Может быть, я плохо искал. Но с моей колокольни все обстоит именно так. И когда передо мной в очередной раз встала задача «Объяснить XSLT», в голове и родилась идея крохотного сервиса. После того, как все заработало, было решено выкатить его для всеобщего пользования: наверняка не я один сталкивался с похожими проблемами.
Впрочем, чего разглядывать картинки, можно пойти и пощупать это все живьем .
Сервис, впрочем, может пригодиться не только в образовательных целях. Заковыристые запросы очень часто бывает удобно наблюдать визуально. Со своей задачей сервис справился. Буду рад, если поможет кому-нибудь еще.