Задать вопрос

Как правильно составить XPath?

Здравствуйте. Ответьте пожалуйста. Есть таблица (table). Мне нужно выбрать из неё все строки.
Из каждой строки выбрать div с классом name , div с классом description и div с классом rating.

Как это сделать? Я думаю, что надо сделать несколько xpath запросов. Один для получения всех строк, а остальные для получения отдельных элементов (в настоящем случае не всё так просто, там целая HTML каша).
Хотел сделать так, но DomXPath требует обьект DomDocument и ничего другого:
$pageDom = new DOMDocument();
        @$pageDom->loadHTML($pageHtml);
        $pageXPath = new DomXPath($pageDom);
        $elementsDom = $pageXPath->query('table/tr');
        // Process all elements
        foreach ($elementsDom as $elementDom) {
        // Здесь ошибка
        $elementXPath = new DomXPath($elementDom);

        $element = array();
        $element['name'] = $elementXPath->query('div[class="name"]')->item(0)->nodeValue;
        $element['description'] = $elementXPath->query('div[class="description"]')->item(0)->nodeValue;
        $element['rating'] = $elementXPath->query('div[class="rating"]')->item(0)->nodeValue;

        $elements[] = $element;
        }
  • Вопрос задан
  • 4012 просмотров
Подписаться 4 Оценить Комментировать
Решения вопроса 1
nowm
@nowm
С самого начала у вас должно быть понимание, что Query можно вызывать только для DomXPath, который инициализируется только с DomDocument. Всё. Ему нельзя подсовывать DomNodeList или DomNode. Только DomDocument. Из-за этого нужно применять другой подход к получению данных.

Вы думаете, что можно найти запросом таблицу, потом ещё одним запросом найти в ней DIV, потом ещё одним запросом найти в этом DIV какой-нибудь SPAN, а в нём ещё одним запросом найти A. С DomXPath так нельзя работать. Хотите найти элемент, ищите его сразу — от корня DOM.

Прямо сейчас могу что-то неточно написать в самих XPath-запросах, но делать нужно примерно так:

$pageDom = new DOMDocument();
@$pageDom->loadHTML($pageHtml);
$pageXPath = new DomXPath($pageDom);

$elementsName = $pageXPath->query('.//table/.//div[class="name"]');
$elementsDescription = $pageXPath->query('.//table/.//div[class="description"]');
$elementsRating = $pageXPath->query('.//table/.//div[class="rating"]');

$elements = array();

for ($i = 0; $i < $elementsName->length; $i++) {
    $elements[] = array(
        'name' => $elementsName->item($i)->nodeValue,
        'description' => $elementsDescription->item($i)->nodeValue,
        'rating' => $elementsRating->item($i)->nodeValue,
    );
}

//Profit


Однако!

Якориться к предыдущим результатам поиска всё-таки возможно. У функции DomXPath::query есть необязательный параметр с типом DOMNode. Получаются такие неявные под-запросы.

$pageDom = new DOMDocument();
@$pageDom->loadHTML($pageHtml);
$pageXPath = new DomXPath($pageDom);

$elementsDom = $pageXPath->query('.//table/tr');

$elements = array();

foreach ($elementsDom as $elementDom) {
    $elements[] = array(
        'name' => $pageXPath->query('.//div[class="name"]', $elementDom)->item(0)->nodeValue,
        'description' => $pageXPath->query('.//div[class="description"]', $elementDom)->item(0)->nodeValue,
        'rating' => $pageXPath->query('.//div[class="rating"]', $elementDom)->item(0)->nodeValue,
    );
}


Особенность в том, что используется всё тот же $pageXPath, а не происходит попытка создать из DOMNode отдельный DOMXPath. И дальше происходит поиск в контексте предыдущих результатов запроса — за счёт добавления в функцию DomXPath::query дополнительного параметра, уточняющего контекст, в котором происходит поиск — DomXPath::query(строка_запроса, контекст_поиска). Так что в такой ситуации «.//div[class="name"]» будет искаться не во всём документе, а только в текущей строке TR.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы