Встроеная в PHP библиотека XPath
Библиотека XPath
это язык запросов для обращения к объектам дерева DOM, структыры xml и html документа.
Запросы можно формировать исходя из разных критериев элементов:
- По классам
- По id
- По именам тегов
- По атрибутам
Например можно выбрать все ссылки по тегу <a ..>
на странице, получив объект содержащий элементы и дальше продолжить работу по средствам PHP.
Синтаксис XPath
Запись выражений XPath
зависит от того какой элемент нужно найти, базовый синтаксис:
XPATH=//tagname[@attribute='value']
Абсолютный путь поиска начинается с одного слэша /
и указывает на полный путь из корневого узла к целевому:
XPATH=/html/body/div[1]/h1
Вырожение означает: выбрать первый div-элемент, под html-элементом, под body-элементом, далее выбрать вложенный h1-элемент.
Относительный путь поиска начинается с двойного слэша //
и указывает на путь из любого узла который отвечает критериям, к целевому узлу:
XPATH=//div[@id='main']
Вырожение означает: выбрать любой div-элемент, имеющий id-атрибут со значением main
.
Основные понятия парсинга XPath
В работе с XPath
нужно знать несколько основных терминов — они описывают, как структурируется документ.
Узлы
Узлами называют разнообразные части страницы или документа:
- Отдельные элементы и записи
- Тексты
- Атрибуты
- Инструкции для обработки какой-то информации
- Комментарии
- Пространства имен
- Другие сущности на странице.
Предки и потомки
Структура иерархична: от корневого элемента как бы «отпочковываются» другие, зависящие от него. У узлов внутри структуры есть предки и потомки:
предки
узлы, которые находятся «выше» и от которых зависит конкретный узел. Частный случай — родитель, узел на один «уровень» выше. У каждого элемента только один родительпотомки
узлы, которые зависят и наследуются от конкретного узла. У элемента может быть ноль, один или больше потомков
Атомарные узлы
Это узлы, которые не зависят от других, не имеют ни потомков, ни родителей. Они фактически одиночные. Как пример — блок текста на странице, не связанный с какими-то другими узлами.
Получение элементов
Синтаксис XPath
описывает правила для создания выражений, которые позволяют точно указать, какие элементы и атрибуты должны быть выбраны из структуры при помощи:
- Выбор узла, подробнее
- Пути выбора, подробнее
- Функций, подробнее
- Оси направления выбора, подробнее
- Логические операторы, подробнее
- Предикаты или коллекции, подробнее
Выбор узла
XPath
представляет документ в виде дерева состоящего из:
- Корневой узел
- Узел элемента
- Узел атрибута
- Текстовый узел
Чтобы выбрать нужный узел, нужно обратиться к нему при помощи языка запросов XPath
, указав путь к нужному элементу. Узел выбирается следуя по заданному пути:
*
|
Выбрать любой элемент |
[]
|
Найти конкретный элемент, например: li[1]имя_узла выбирает все узлы с указанным именем узла. за место li , может быть любой тэг div , p и т.д. |
/
|
Ищет от корневого узла |
//
|
Ищет узлы в документе от текущего узла, который соответствует выбору, независимо от того, где они находятся |
.
|
Ищет текущий узел |
..
|
Ищет родителя текущего узла |
@
|
Ищет нужный атрибут, например: p[@value="2024"] |
Абсолютные и относительные пути обращения к элементам
К конкретному элементу можно обратиться двумя способами, которые отличаются путями, есть абсолютный путь и относительный путь.
Абсолютный путь начинается со слеша /
и указывает на полный путь от корневого узла до целевого узла:
/html/body/div[1]/h1
Вырожение выше означает что нужно выбрать первый элемент div
, который находится по пути html > body > div
и затем выбрать его дочерний элемент h1
.
Относительный путь начинается с двух слешей //
и указывает на путь от любого узла, который соответствует определенным критериям, до целевого узла:
//div[@id='main']
Вырожение выше означает что нужно выбрать любой элемент div
, который имеет атрибут id
со значением main
.
Функции XPath
Язык XPath
имеет набор встроенных функций, которые позволяют осуществлять различные операции. Вот некоторые из основных функций:
text()
|
Возвращает текстовое содержимое элемента |
count()
|
Возвращает количество элементов, соответствующих указанному выражению |
normalize-space()
|
Удаляет лишние пробелы из строки и заменяет последовательности пробелов на одиночные пробелы |
starts-with(x,y)
|
Проверяет, начинается ли строка с x-y |
contains(x,y)
|
Проверяет, содержит ли строка x-y |
last()
|
Возвращает последнюю позицию элемента в выборке |
position()
|
Возвращает позицию текущего элемента в выборке |
name()
|
Возвращает имя текущего элемента |
sum()
|
Суммирует значения элементов выборки |
string()
|
Преобразует узел в строку |
lower-case()
|
Преобразует текст в нижний регистр |
@attribute
|
Выбирает значение указанного атрибута |
concat()
|
Объединяет две или более строки |
string-length()
|
Возвращает длину строки |
substring()
|
Возвращает подстроку из строки, начиная с указанной позиции |
Кроме основных функций, XPath
также предоставляет более сложные математические функции, а также функции работы с датами и временем. Ниже преведины примеры.
Выбрать любой h1-элемент, имеющий текстовое значение Welcome:
//h1[text()='Welcome']
Выбрать любой элемент p
, имеющий значение Hello
:
//p[contains(text(),'Hello')]
Выбрать любой рисунок с атрибутом src
, который начинается со значения logo
:
//img[starts-with(@src,'logo')]
Выбрать любой элемент span
, который заканчивается значением _title
:
//span[ends-with(@id,'_title')]
Оси XPath
Ось в XPath
это специальная концепция, которая используется для указания направления движения при поиске элементов в документе. Оси позволяют выбирать элементы, которые относятся к определенным отношениям с другими элементами в дереве. Есть несколько осей, которые можно использовать при создании выражений:
parent
, выбирает родителя текущего узла подробнееancestor
, выбирает всех предков текущего узла подробнееancestor-or-self
, выбирает всех предков текущего узла и сам текущий узел подробнееchild
, выбирает всех прямых потомков текущего узла подробнееdescendant
, выбирает всех потомков текущего узла подробнееfollowing
, выбирает все элементы в документе после закрывающего тега текущего узла подробнееfollowing-sibling
, выбирает все элементы одного уровня после текущего узла подробнееpreceding
, выбирает все узлы, которые появляются перед текущим узлом, за исключением предков, узлов атрибутов и пространства имен подробнееpreceding-sibling
, выбирает все элементы одного уровня до текущего узла подробнее
Синтаксис оси XPath
:
Axes::tagName[@attribute='value']
Parent
Выбирает верхний (родительский) узел (по отношению к текущему узлу):
//div[@id='Y2']/parent::*
Выбирет любой элемент, являющийся прямым родительским для div-элемента с id Y2
, это B2
в нашем случае.
Ancestor
Выбирает всех предков текущего узла. Иными словами, первый родительский и все старше, то есть «по направлению к корням дерева»:
//div[@id='Y2']/ancestor::div[@id='B2']
Выбирает div-элемент с id B2
, который является предком div-элемента с id Y2
.
//div[@id='Y2']/ancestor::div[@id='A']
Выбирает div-элемент с id A
, являющийся предком div-элемента с id Y2
.
Ancestor-or-self
Вибирает все предковые элементы текущего узла, включая и сам текущий узел:
То есть то же самое что Ancestor
, и плюс сам этот узел.
Child
Выбирает непосредственные элементы-потомки (но только первые дочерние) текущего узла:
//div[@id='B2']/child::*
Выбирает все элементы, являющиеся прямыми потомками div-элемента с id B2
.
//div[@id='B2']/child::div[@id='Y2']
выбор всех div-элементов с id Y2
, прямых потомков div-элемента с id B2
.
Descendant
Выбрать всех потомков («во всех коленах») текущего узла:
//div[@id='B2']/descendant::*
Выбирает все элементы, которые являются потомками div-элемента с id B2
.
//div[@id='B2']/descendant::div[@id='L1']
Выбирает div-элементы-потомки от div-элемента с id L1
.
Following
Выбрать все элементы в документе, идущие после закрывающего тега текущего узла. Другими словами, ниже текущего элемента по дереву (на всех уровнях и слоях), но исключая собственных потомков (смотрим рисунок):
//div[@id='B2']/following::*
Выбирает все div-элементы ниже текущего элемента с id B2
.
Following-sibling
Все элементы «братского», то есть того же уровня:
//div[@id='B2']/following-sibling::*
Выбирает все элементы на том же уровне что и div-элемент с id B2
, и ниже его в дереве.
Preceding
Preceding аналогичен Following, но в обратную сторону, то есть все элементы выше указанного.
//div[@id='B2']/preceding::*
Выбирает все элементы идущие перед div-элементом с id B2
.
Preceding-sibling
Аналогично Preceding, но выбирает только на том же уровне и перед указанным, иными словами любой элемент с тем же родителем и на том же уровне.
Логические операторы
В XPath
широко применяются логические операторы для указания нескольких условий поиска и сочетания:
AND
OR
NOT
Например:
//input[@type='text' AND @name='username']
Означает выбрать любой input
имеющий одновременно атрибут типа со значением text
и атрибут имени со значением username
.
//a[@href='https://testengineer.ru' OR @class='link']
Означает выбрать любой якорный элемент, имеющий или href-атрибут со значением https://testengineer.ru
, или с атрибутом класса со значением link
.
//div[NOT(@id)]
Означает выбрать любой div-элемент, не имеющий id-атрибута.
Предикаты или коллекции
Предикаты или коллекции представляют собой конструкции, которые используются для фильтрации элементов и выбора конкретных элементов с помощью определенных условий. Вот несколько примеров использования предикатов:
//a
|
Выберите все элементы в документе |
//a[@class='active']
|
Выберите все элементы, у которых атрибут class равен "active" |
//input[@type='checkbox']
|
Выберите все элементы input, у которых атрибут type равен "checkbox" |
//input[@type='text']
|
Выберите все элементы input с атрибутом type равным "text" |
//p[contains(text(), 'Lorem')]
|
Выберите все элементы, у которых текст содержит слово "Lorem" |
//div[count(p) > 3]
|
Выберите все элементы, у которых количество дочерних элементов больше 3 |
//a[starts-with(@href, 'https://')]
|
Выберите все элементы, у которых атрибут href начинается с "https://" |
//input[matches(@value, '^\d+$')]
|
Выберите все элементы input, у которых атрибут value содержит только цифры |
//p[@value="01/2008"]
|
Выбирает все элементы p, у которых есть атрибут value со значением "01/2008" |
//p[@value]
|
Выбирает все элементы p, у которых есть атрибут value |
//p/text()
|
Выделит все текстовые узлы внутри всех элементов p |
//div[not(p)]
|
Выберите все элементы, у которых не существует дочернего элемента |
/div/p[position()<3]
|
Выбирает первые два элемента p, которые являются прямыми потомками элемента div |
//tag[position()=1]
|
Выбирает первый элемент с тегом "tag" |
/div/ul/li[1]
|
Выбирает первый элемент li, который является прямым потомком элемента ul в div |
//li[a]
|
Выделяет элементы li, в которых есть элемент a |
//li[last()]
|
Выделяет последний элемент li в документе |
//a | //h2
|
Выделить все элементы a и h2 с помощью оператора объединения | |
//tag[@value > 9]
|
Получить узлы tag, value которых больше 9-ти |
//div[note[@value > 9]]/а
|
Получить только имена узлов, value которых больше 9-ти |
//div[4]/h2[text() = "Текст"]
|
Выделит четвертый элемент div, h2 которого содержит слово: Текст |
/div/note[last()]
|
Выбирает последний элемент note, который является прямым потомком элемента div |
/div/note[last()-1]
|
Выбирает предпоследний элемент note, который является прямым потомком элемента div |
//*[@id]
|
Выберите все элементы с атрибутом id |
//div[contains(@class, 'content')]/p
|
Выберите все элементы, которые являются дочерними элементами с классом "content" |
//img[contains(@src, 'logo')]
|
Выберите все элементы , у которых атрибут src содержит слово "logo" |
//*[@href[contains(text(), '.pdf')]]
|
Выберите все элементы со значением атрибута href, оканчивающимся на .pdf |
//*[@data-toggle='modal']
|
Выберите все элементы с атрибутом data-toggle и значением "modal" |
//tag[@attribute>5]
|
Выбирает все элементы с тегом "tag" и атрибутом "attribute", значение которого больше 5 |
//tag[@attribute="value"]
|
Выбирает все элементы с тегом "tag" и атрибутом "attribute" со значением "value" |
Составление пути XPath
Составить формулу можно при помощи консоли разработчика в браузере:
Парсинг HTML в XPath
Используя XPath
получим из DOM дерева моего сайта нужный нам объект, мы будем получать со страницы hmarketing.ru призыв к действию:
<?
// создадим запрос на получение страницы сайта
$data = file_get_contents('https://hmarketing.ru');
// создаем конструктор domDocument
$dom = new domDocument;
// загружаем HTML
$dom->loadHTML($data);
// сохраняем полученный HTML
$dom->saveHTML();
// создаем конструктор DomXPath и передаем параметром HTML
$xpath = new DomXPath($dom);
// ищем нужный блок
$xpathData = $xpath->query("//*[@class='col tekst']");
// получаем элемент, их может быть много и тогда нужно использовать цикл
$xpathItem = $xpathData->item(0);
// выводим результат
echo $xpathItem->textContent;
?>