Используйте XPath с PHP SimpleXML, чтобы найти узлы, содержащие строку - PullRequest
4 голосов
/ 16 сентября 2010

Я пытаюсь использовать SimpleXML в сочетании с XPath для поиска узлов, которые содержат определенную строку.

<?php
$xhtml = <<<EOC
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <title>Test</title>
    </head>
    <body>
        <p>Find me!</p>
        <p>
            <br />
            Find me!
            <br />
        </p>
    </body>
</html>
EOC;

$xml = simplexml_load_string($xhtml);
$xml->registerXPathNamespace('xhtml', 'http://www.w3.org/1999/xhtml');

$nodes = $xml->xpath("//*[contains(text(), 'Find me')]");

echo count($nodes);

Ожидаемый вывод: 2 Фактический вывод: 1

Когда я изменяю xhtmlвторой абзац до

<p>
    Find me!
    <br />
 </p>

тогда он работает как положено.Как мое выражение XPath должно выглядеть так, чтобы соответствовать всем узлам, содержащим «Найди меня», независимо от того, где они находятся?

Использование PHP в DOM-XML является опцией, но не желательным.

Спасибозаранее!

Ответы [ 4 ]

9 голосов
/ 16 сентября 2010

Это зависит от того, что вы хотите сделать. Вы можете выбрать все элементы <p/>, которые содержат «Find me» в любом из их потомков, с помощью

//xhtml:p[contains(., 'Find me')]

Это вернет дубликаты, поэтому вы не будете указывать тип узлов, тогда он также вернет <body/> и <html/>.

Или, может быть, вам нужен любой узел, у которого есть дочерний (не потомковый) текстовый узел, содержащий «Найди меня»

//*[text()[contains(., 'Find me')]]

Этот не вернет <html/> или <body/>.


Я забыл упомянуть, что . представляет все текстовое содержимое узла. text() используется для извлечения [набора узлов] текстовых узлов. Проблема с вашим выражением contains(text(), 'Find me') состоит в том, что contains() работает только со строками, но не с наборами узлов, и поэтому он преобразует text() в значение первого узла, поэтому удаление первого <br/> заставляет его работать.

1 голос
/ 15 августа 2012

Я искал способ выяснить, существует ли узел с точным значением «Найти меня», и это, похоже, работает.

$node = $xml->xpath("//text()[.='Find Me']");
1 голос
/ 16 сентября 2010

ммм? Но спасибо @Jordy за быстрый ответ.

Во-первых, это DOM-XML, что нежелательно, поскольку все остальное в моем скрипте выполняется с помощью SimpleXML.

Во-вторых, почему вы переводите в верхний регистр и ищете неизменную строку «Найди меня»? Поиск «НАЙТИ МЕНЯ» на самом деле даст результат.

Но вы указали мне правильное направление:

$nodes = $xml->xpath("//text()[contains(., 'Find me')]");

делает трюк!

0 голосов
/ 16 сентября 2010
    $doc = new DOMDocument();
    $doc->loadHTML($xhtml);

    $xPath = new DOMXpath($doc);
    $xPathQuery = "//text()[contains(translate(.,'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'Find me')]";
    $elements = $xPath->query($xPathQuery);

    if($elements->length > 0){

    foreach($elements as $element){
        print "Found: " .$element->nodeValue."<br />";
    }}
...