Xpath php не работает

использование xpath в php не работает с файлом

Применение xpath() в объекте SimpleXMLElement, загружаемое XML из строки, работает. Если я загружу его из файла — это не. Я просто получаю пустой массив.

Уровень глубины элемента, который я ищу, равен 5 (если мы говорим, что корневой узел — 1-й уровень). Когда на xml из строки — он работает даже с выражением xpath, которое начинается с последнего уровня. Когда на xml из файла — никакой xpath работает вообще.

Способы загрузки содержимого xml в объект SimpleXMLElement (и применение xpath() ):

Не удалось, оба из них. Когда из строки просто $xml = simplexml_load_string($xmlStr); работал отлично. Я просмотрел ЭТУ СТАТЬЮ и, вероятно, попытался почти любое предложение из комментариев, которые казались разумными для моего дела.

Еще одна странная вещь, которую я заметил, заключается в том, что когда я записываю в журнал $xml , когда он из строки, я получаю вывод (массивов и объектов и т.д.) Из уровня глубины Product (который является вторым). Но когда я делаю это с $xml , который был загружен из файла, я получаю вывод из корневого узла.

Простое представление XML-кода:

Поэтому мой вопрос: как я работаю, когда загружаю XML из файла, поскольку он работает, когда я загружаю его из строки?

PS Я не знаю, относится ли это к текущей проблеме, но — когда я загружаюсь из строки (heredoc), а строка имеет первую строку — Я получаю ошибку вызова метода xpath() для non-object . Но как только я удаляю эту первую строку, и строка начинается с корневого узла — все безупречно.

UPDATE: первый узел :

ОБНОВЛЕНИЕ 2: вся работа с загрузкой из файла не работает только из-за 1-го атрибута в как здесь: , поэтому вся проблема находится в пространстве имен.

Источник

Почему не работает xpath?

Хочу спарсить таблицу, код

echo не вьіводит ничего.

  • Вопрос задан более трёх лет назад
  • 3886 просмотров

В первую очередь — из-за этого:

«load» — это для загрузки файлов и в качестве параметра ей нужно давать путь к файлу. Если вы хотите загрузить строку, нужно использовать функцию «loadHTML».

Дальше у вас появится куча предупреждений. Если появится сообщение про то, что непонятки с кодировкой появились, от него можно избавиться, если поправить строку с loadHTML:

Кроме строки про кодировку будет ещё куча предупреждений, вроде:

Чтобы эти уведомления не засоряли эфир, можно добавить символ «@» при вызове «loadHTML»:

Дальше, чтобы удостовериться, что те узлы, которые вы пытаетесь искать, всё-таки существуют, можно вывести список вообще всех узлов, вот так:

Из листинга будет видно, что упоминание связки «table/tbody/tr» некорректно. «TBODY» там нет. Такой XPath-запрос сработает нормально в FirePath из Firefox, например. И работает он из-за того, что Firefox самостоятельно достраивает DOM документа до идеального по его мнению состояния — например, добавляет «TBODY», где его нет, закрывает незакрытые теги и так далее.

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

В вашей ситуации нужно из запроса просто убрать «tbody». Получится такой запрос:

Как я вижу, решение уже появилось, но, вообще, такой подход, который я описал, поможет искать ошибки в подобных ситуациях.

Источник

Примеры xpath-запросов к html

Создание запроса к узлам веб-страниц

Предлагаю вашему вниманию небольшую лабораторную работу, в ходе которой я продемонстрирую создание xpath запросов к веб-странице. Вы сможете повторить приведенные мной запросы и, самое главное, попробуете выполнить свои. Я надеюсь, что благодаря этому статья будет одинаково интересна новичкам и программистам знакомым с xpath по xml.

Для лабораторной нам понадобятся:
— веб-страница xhtml;
— браузер Mozilla Firefox с дополнениями;
— firebug;
— firePath;
(вы можете использовать любой другой браузер с визуальной поддержкой xpath)
— немного времени.

В качестве веб-страницы для проведения эксперимента предлагаю главную страницу сайта консорциума всемирной паутины (‘http://w3.org’). Именно эта организация разрабатывает языки xquery(xpath), спецификацию xhtml и многие другие стандарты интернета.

Задача

Получить из xhtml-кода главной страницы w3.org информацию о конференциях консорциума при помощи запросов xpath.
Приступим к написанию xpath запросов.

Первый Xpath запрос

Открываем закладку Firepath в FireBug, выделяем с селектором элемент для анализа, нажимаем: Firepath создал xpath запрос к выбранному элементу.

Читайте также:  Локальная сеть не работает с роутером

Если вы выделили заголовок первого события, то запрос будет таким:

После удаления лишних индексов запрос станет соответствовать всем элементам типа «заголовок».

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

Идем дальше. Создаем запросы для поиска мест проведения конференций и их спонсоров либо с помощью селектора, либо модифицировав первый запрос.

Запрос для получения информации о местах проведения конференций:
.//*[@id=’w3c_home_upcoming_events’]/ul/li/div/p[2]

Так мы получим список спонсоров:
.//*[@id=’w3c_home_upcoming_events’]/ul/li/div/p[3]

Синтаксис xpath

Давайте вернемся к созданным запросам и разберемся в том, как они устроены.
Рассмотрим подробно первый запрос

В этом запросе я выделил три части для демонстрации возможностей xpath. (Деление на части уловное)

Первая часть
.// — рекурсивный спуск на ноль или более уровней иерархии от текущего контекста. В нашем случае текущий контекст это корень документа

Вторая часть
* — любой элемент,
[@id=’w3c_home_upcoming_events’] – предикат, на основе которого осуществляем поиск узла, имеющего атрибут id равным ‘w3c_home_upcoming_events’. Идентификаторы элементов XHTML должны быть уникальны. Поэтому запрос «любой элемент с конкретным ID» должен вернуть единственный искомый нами узел.

Мы можем заменить * на точное имя узла div в этом запросе
div[@id=’w3c_home_upcoming_events’]

Таким образом, мы спускаемся по дереву документа до нужного нам узла div[@id=’w3c_home_upcoming_events’]. Нас абсолютно не волнует, из каких узлов состоит DOM-дерево и сколько уровней иерархии осталось выше.

Третья часть
/ul/li/div/p/a –xpath-путь до конкретного элемента. Путь состоит из шагов адресации и условия проверки узлов (ul, li и т.д.). Шаги разделяются символом » /»(косая черта).

Коллекции xpath

Не всегда удается получить доступ к интересующему узлу с помощью предиката или шагов адресации. Очень часто на одном уровне иерархии находится насколько узлов одинакового типа и необходимо выбрать «только первые» или «только вторые» узлы. Для таких случаев предусмотрены коллекции.

Коллекции xpath позволяют получить доступ к элементу по его индексу. Индексы соответствуют тому порядку, в котором элементы были представлены в исходном документе. Порядковый номер в коллекциях отсчитывается от единицы.

Исходя из того, что «место проведения» всегда второй параграф после «названия конференции», получаем следующий запрос:
.//*[@id=’w3c_home_upcoming_events’]/ul/li/div/p[2]
Где p[2] – второй элемент в наборе для каждого узла списка /ul/li/div.

Аналогично список спонсоров мы можем получить запросом:
.//*[@id=’w3c_home_upcoming_events’]/ul/li/div/p[3]

Некоторые функции хpath

В хpath существует множество функций для работы с элементами внутри коллекции. Я приведу только некоторые из них.

last():
Возвращает последний элемент коллекции.
Запрос ul/li/div/p[last()] — возвратит последние параграфы для каждого узла списка «ul».
Функция first() не предусмотрена. Для доступа к первому элементу используйте индекс «1».

text():
Возвращает тестовое содержание элемента.
.//a[text() = ‘Archive’] – получаем все ссылки с текстом «Archive».

position() и mod:
position() — возвращает позицию элемента в множестве.
mod — остаток от деления.

Комбинацией данных функций можем получить:
— не четные элементы ul/li[position() mod 2 = 1]
— четные элементы: ul/li[position() mod 2 = 0]

Операции сравнения

  • — логическое «больше»
  • = — логическое «больше либо равно»

ul/li[position() > 2] , ul/li[position() — элементы списка начиная с 3го номера и наоборот.

Самостоятельно

Попробуйте получить:
— четные URL ссылки из левого меню «Standards»;
— заголовки всех новостей, кроме первой с главной страницы w3c.org.

Xpath в PHP5

В заключение

На простом примере мы увидели возможности xpath для доступа к узлам веб-страниц.
Xpath является отраслевым стандартом для доступа к элементам xml и xhtml, xslt преобразований.
Вы можете применять его для парсинга любой html-страницы. В случае если исходный html-код содержит значительные ошибки в разметке пропустите его через tidy. Ошибки будут исправлены.

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

Источник

SimpleXMLElement::xpath

(PHP 5, PHP 7, PHP 8)

SimpleXMLElement::xpath — Запускает запрос XPath к XML-данным

Описание

Метод xpath ищет узлы SimpleXML с дочерними элементами, соответствующие XPath expression .

Список параметров

Возвращаемые значения

Возвращает массив ( array ) объектов SimpleXMLElement в случае успешного выполнения или false в случае возникновения ошибки.

Примеры

Пример #1 Xpath

$xml = new SimpleXMLElement ( $string );

while(list( , $node ) = each ( $result )) <
echo ‘/a/b/c: ‘ , $node , «\n» ;
>

/* Относительные пути также работают. */
$result = $xml -> xpath ( ‘b/c’ );

while(list( , $node ) = each ( $result )) <
echo ‘b/c: ‘ , $node , «\n» ;
>
?>

Результат выполнения данного примера:

Обратите внимание, что оба результата одинаковы.

Смотрите также

  • SimpleXMLElement::registerXPathNamespace() — Создаёт префикс/пространство имён контекста для следующего запроса XPath
  • SimpleXMLElement::getDocNamespaces() — Возвращает пространства имён, объявленные в документе
  • SimpleXMLElement::getNamespaces() — Возвращает пространства имён, используемые в документе
  • Базовое использование SimpleXML

User Contributed Notes 17 notes

To run an xpath query on an XML document that has a namespace, the namespace must be registered with SimpleXMLElement::registerXPathNamespace() before running the query. If the XML document namespace does not include a prefix, you must make up an arbitrary one, and then use it in your query.

Читайте также:  Как взять ипотеку если муж не работает

$xmlDoc =new \ SimpleXMLElement ( $strXml );

foreach( $xmlDoc -> getDocNamespaces () as $strPrefix => $strNamespace ) <
if( strlen ( $strPrefix )== 0 ) <
$strPrefix = «a» ; //Assign an arbitrary namespace prefix.
>
$xmlDoc -> registerXPathNamespace ( $strPrefix , $strNamespace );
>

print( $xmlDoc -> xpath ( «//a:message» )[ 0 ]); //Use the arbitrary namespace prefix in the query.
?>

This will output:

On a xml that have namespace you need to do this before your xpath request (or empty array will be return) :

= str_replace ( ‘xmlns=’ , ‘ns=’ , $string ); //$string is a string that contains xml.
?>

xpath() can also be used to select elements by their attributes. For a good XPath reference check out: http://www.w3schools.com/xpath/xpath_syntax.asp

$xml = simplexml_load_string ( $string );
$result = $xml -> xpath ( «//size[@label=’Large’]» );

// print the first (and only) member of the array
echo $result [ 0 ]-> asXml ();
?>

The script would print:

As mentioned already xpath will fail is the default namespace ‘xmlns’ is used like in:

= $string =

XML;
?>

xpath cannot search through the xml without explicitly specifying a namespace.
There are 2 options :
1. rename the ‘xmlns’ into something else to trick xpath into believing that no default namespace is defined.
2. register a string as the default namespace and use that string in all your queries. Unfortunatly, an empty space will not work.
No other option currently exist until XPath2.0 becomes the default library.

If you want to find easly all records satisfying some condition in XML data like

try example below

= file_get_contents ( ‘data/books.xml’ );
$xml = new SimpleXMLElement ( $xmlStr );
// seach records by tag value:
// find all book records with price higher than 40$
$res = $xml -> xpath ( «book/price[.>’40’]/parent::*» );
print_r ( $res );

?>

You will see response like:
Array (
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[id] => bk101
)

[author] => Gambardella, Matthew
[title] => XML Developer’s Guide
[genre] => Computer
[price] => 44.95
[publish_date] => 2000-10-01
[description] => An in-depth look at creating applications
with XML.
)
.

Xpath actually knows which element is root regardless of element on which you execute it. Example:

header ( ‘content-type: text/plain’ );

$xml = new SimpleXMLElement ( $string );

//relative to root
$b0 = $xml -> b [ 0 ]-> xpath ( ‘//c’ );
while(list( , $node ) = each ( $b0 )) <
echo ‘b[0]: //c: ‘ , $node , «\n» ;
>

$b1 = $xml -> b [ 1 ]-> xpath ( ‘//c’ );
while(list( , $node ) = each ( $b1 )) <
echo ‘b[1]: //c: ‘ , $node , «\n» ;
>

//relative to current element
$b0 = $xml -> b [ 0 ]-> xpath ( ‘.//c’ );
while(list( , $node ) = each ( $b0 )) <
echo ‘b[0]: .//c: ‘ , $node , «\n» ;
>

$b1 = $xml -> b [ 1 ]-> xpath ( ‘.//c’ );
while(list( , $node ) = each ( $b1 )) <
echo ‘b[1]: .//c: ‘ , $node , «\n» ;
>

Источник

DOMXPath::query

(PHP 5, PHP 7, PHP 8)

DOMXPath::query — Выполняет заданное выражение XPath

Описание

Выполняет заданное XPath-выражение expression .

Список параметров

Выражение XPath для выполнения.

Дополнительный параметр contextNode может быть указан для выполнения относительных запросов XPath. По умолчанию запросы выполняются относительно корневого элемента.

Дополнительный параметр registerNodeNS можно указать, чтобы отключить автоматическую регистрацию контекста узла.

Возвращаемые значения

Возвращает объект DOMNodeList , содержащий узлы, соответствующие выражению XPath expression . Любое выражение, не возвращающее узлы, вернёт пустой объект DOMNodeList .

Если expression построено неправильно или contextNode имеет неверное значение, DOMXPath::query() вернёт false .

Примеры

Пример #1 Получение списка всех книг на английском

// Не хотим возиться с пробелами
$doc -> preserveWhiteSpace = false ;

$doc -> load ( ‘book.xml’ );

$xpath = new DOMXPath ( $doc );

// Начинаем с корневого элемента
$query = ‘//book/chapter/para/informaltable/tgroup/tbody/row/entry[. = «en»]’ ;

$entries = $xpath -> query ( $query );

foreach ( $entries as $entry ) <
echo «Найдена книга < $entry ->previousSibling -> previousSibling -> nodeValue > ,» .
» автор < $entry ->previousSibling -> nodeValue > \n» ;
>
?>

Результат выполнения данного примера:

Можно также использовать параметр contextNode для более короткой записи выражения:

= new DOMDocument ;
$doc -> preserveWhiteSpace = false ;

$doc -> load ( ‘book.xml’ );

$xpath = new DOMXPath ( $doc );

$tbody = $doc -> getElementsByTagName ( ‘tbody’ )-> item ( 0 );

// запрос относительно узла tbody
$query = ‘row/entry[. = «en»]’ ;

$entries = $xpath -> query ( $query , $tbody );

foreach ( $entries as $entry ) <
echo «Найдена книга < $entry ->previousSibling -> previousSibling -> nodeValue > ,» .
» автор < $entry ->previousSibling -> nodeValue > \n» ;
>
?>

Смотрите также

  • DOMXPath::evaluate() — Вычисляет переданное выражение XPath и возвращает типизированный результат, если возможно
Читайте также:  До какого числа не работают базы отдыха

User Contributed Notes 18 notes

If the query() function seems to ignore your $contextnode, and instead returns all the tags in the document, try to use a relative path (use a . in front of the query):

$dom = new DomDocument ();
$dom -> loadXML ( $xml );
$xpath = new DomXPath ( $dom );

$tag1 = $dom -> getElementsByTagName ( «tag1» )-> item ( 0 );

echo $xpath -> query ( «//tag2» )-> length ; //output 2 -> correct
echo $xpath -> query ( «//tag2» , $tag1 )-> length ; //output 2 -> wrong, the query is not relative
echo $xpath -> query ( «.//tag2» , $tag1 )-> length ; //output 1 -> correct (note the dot in front of //)
?>

See that i couldn’t use $xpath->query(«tag2», $tag1) as per the documentation, since «tag2» is not a direct child of «tag1».
I don’t know why this note was deleted, i just tested it and it’s correct.
It’s not a bug, it’s simply not written in the documentation.

Please note that what clochix says is valid for *any* document which has a default namespace (as it is the case for XHTML).

must be accessed this way :

$document = new DOMDocument();
$document->load(‘document.xml’);

$xpath = new DOMXPath($document);
$xpath->registerNameSpace(‘fakeprefix’, ‘http://www.exemple.org/namespace’);

Of course, there is no prefix in the original document, but the DOMXPath class *needs* one, whatever it is, if you use a default namespace. It *doesn’t work* if you specify an empty prefix like this :

Hope this help to spare some time.

Пример XPath запроса к XML документу. XML документ содержить элементы с именами из НЕлатинских символов (кириллица). При использовании в XPath запросе предиката, функция DOMXPath::query() выдает предупреждение и запрос не работает. Чтобы запрос работал, надо явно указывать ось. Файл этого примера должен быть в кодировке WINDOWS-1251. Тестировал в PHP 5.2.9-2 и PHP 5.2.17

Example XPath-query to the XML-document. XML-document contains an elements with the names of non-Latin characters (cyrillic). When used predicate in XPath-query, function DOMXPath::query() gives a warning and query does not work. In order to earned the query, it is necessary to explicitly specify the axis. The file of this example is to be in the encoding WINDOWS-1251. Tested in PHP 5.2.9-2 and PHP 5.2.17
( «display_errors» , «on» );
error_reporting (- 1 );
function utf8encode ( $str )
$xml = »

Яндекс
г.Донецк

» ;
$document =new domDocument ();
$document -> preserveWhiteSpace = false ;
$document -> loadXML ( $xml );
$domxpath =new domXpath ( $document );
$list = $domxpath -> query ( utf8encode ( ‘/child::часть/child::ссылка’ )); //Ok
echo ‘$list->length=’ . $list -> length . «\n
\n» ;
$list = $domxpath -> query ( utf8encode ( ‘/часть/ссылка’ )); //Ok
echo ‘$list->length=’ . $list -> length . «\n
\n» ;
$list = $domxpath -> query ( utf8encode ( ‘/child::часть/child::ссылка[position()=1]’ )); //Ok
echo ‘$list->length=’ . $list -> length . «\n
\n» ;
$list = $domxpath -> query ( utf8encode ( ‘/часть/ссылка[1]’ )); //Warning: DOMXPath::query() [domxpath.query]: Invalid expression in .
echo ‘$list->length=’ . $list -> length . «\n
\n» ;
$list = $domxpath -> query ( utf8encode ( ‘/часть/ссылка[position()=1]’ )); //Warning: DOMXPath::query() [domxpath.query]: Invalid expression in .
echo ‘$list->length=’ . $list -> length . «\n
\n» ;
?>

If you’re wondering, like I was, why your XPath queries are not returning any of the new DOMElements you create in your (X)HTML documents, and only the ones originally loaded in with (for example) loadXML(), this is why; if you’re doing things right, you have registered the nameSpace ‘html’ after creating your DOMXPath object thus:

class XPathQueryLength <
private $nameSpace = » ;
function __construct ( DOMDocument $doc ) <
$this -> xpath = new DOMXPath ( $this -> doc );
$this -> xpath -> registerNamespace (
‘html’ , ‘http://www.w3.org/1999/xhtml’ );
>
function queryLength ( $query ) <
return $this -> xpath -> query ( $query )-> length ;
>
>

?>

. but don’t forget that when adding new elements to the above DOMDocument $doc, to use createElementNS() instead of createElement(), otherwise you’ll have this problem:

//$doc is a previously loaded XHTML document containing a normal html, head and body structure
//$body is the first selected tag using $doc->getElementsByTagName(‘body’);

$pTag = $doc -> createElement ( ‘p’ , ‘This is a new paragraph!’ );
$body -> appendChild ( $pTag );

$xPath = new XPathQueryLength ( $doc );
print $xPath -> queryLength ( ‘//html:p’ );

print $xPath -> queryLength ( ‘//p’ );

?>

So do this instead:

//$doc is a previously loaded XHTML document containing a normal html, head and body structure
//$body is the first selected tag using $doc->getElementsByTagName(‘body’);

$pTag = $doc -> createElementNS ( ‘http://www.w3.org/1999/xhtml’ , ‘p’ , ‘This is a new paragraph!’ );
$body -> appendChild ( $pTag );

$xPath = new XPathQueryLength ( $doc );
print $xPath -> queryLength ( ‘//html:p’ );

print $xPath -> queryLength ( ‘//p’ );

?>

The resulting XHTML file from both example scripts looks much like this:

Источник

Оцените статью