XPath Узел Строка - PullRequest
       30

XPath Узел Строка

2 голосов
/ 04 августа 2010

Как выбрать содержимое строки следующих узлов:

<span class="url">
 word
 <b class=" ">test</b>
</span>

<span class="url">
 word
 <b class=" ">test2</b>
 more words
</span>

Я пробовал несколько вещей

//span/text()

Не получает жирный тег

//span/string(.)

недействительно

string(//span)

выбирает только 1 узел

Я использую simple_xml в php, и я думаю, что единственная другая опция - это использование // span, который возвращает:

Array
(
    [0] => SimpleXMLElement Object
        (
            [@attributes] => Array
                (
                    [class] => url
                )

            [b] => test
        )

    [1] => SimpleXMLElement Object
        (
            [@attributes] => Array
                (
                    [class] => url
                )

            [b] => test2
        )

)

* обратите внимание, что он также удаляет текст «больше слов» из второго промежутка.

Итак, я думаю, что я мог бы затем сгладить элемент в массиве с помощью php, как?Xpath предпочтительнее, но любые другие идеи тоже могут помочь.

Ответы [ 7 ]

4 голосов
/ 05 августа 2010

Вам даже не нужен XPath для этого:

$dom = new DOMDocument;
$dom->loadHTML($html);
foreach($dom->getElementsByTagName('span') as $span) {
    if(in_array('url', explode(' ', $span->getAttribute('class')))) {
        $span->nodeValue = $span->textContent;
    }
}
echo $dom->saveHTML();

РЕДАКТИРОВАТЬ после комментария ниже

Если вы просто хотите извлечь строку, вы можете сделать echo $span->textContent; вместо замены nodeValue. Я понял, что вы хотите иметь одну строку для диапазона вместо вложенной структуры. В этом случае вам также следует подумать, не будет ли простой запуск strip_tags во фрагменте span более быстрой и простой альтернативой.


В PHP5.3 вы также можете зарегистрировать произвольные функции PHP для использования в качестве обратных вызовов в запросах XPath. Следующее извлекает содержимое всех элементов span и его дочерних узлов и возвращает его в виде одной строки.

$dom = new DOMDocument;
$dom->loadHTML($html);
$xp = new DOMXPath($dom);
$xp->registerNamespace("php", "http://php.net/xpath");
$xp->registerPHPFunctions();
echo $xp->evaluate('php:function("nodeTextJoin", //span)');

// Custom Callback function
function nodeTextJoin($nodes)
{
    $text = '';
    foreach($nodes as $node) {
        $text .= $node->textContent;
    }
    return $text;
}
4 голосов
/ 05 августа 2010
$xml = '<foo>
<span class="url">
 word
 <b class=" ">test</b>
</span>

<span class="url">
 word
 <b class=" ">test2</b>
 more words
</span>
</foo>';
$dom = new DOMDocument();
$dom->loadXML($xml); //or load an HTML document with loadHTML()
$x= new DOMXpath($dom);
foreach($x->query("//span[@class='url']") as $node) echo $node->textContent;
2 голосов
/ 05 августа 2010

Использование XMLReader:

$xmlr = new XMLReader;
$xmlr->xml($doc);
while ($xmlr->read()) {
    if (($xmlr->nodeType == XmlReader::ELEMENT) && ($xmlr->name == 'span')) {
        echo $xmlr->readString();
    }
}

Вывод:

word
test

word
test2
more words
1 голос
/ 05 августа 2010

SimpleXML не любит смешивать текстовые узлы с другими элементами, поэтому вы теряете часть контента там.Расширение DOM, однако, справляется с этим просто отлично.К счастью, DOM и SimpleXML - это две стороны одной монеты (libxml), поэтому их очень легко манипулировать.Например:

foreach ($yourSimpleXMLElement->xpath('//span') as $span)
{
    // will not work as expected
    echo $span;

    // will work as expected
    echo textContent($span);
}

function textContent(SimpleXMLElement $node)
{
    return dom_import_simplexml($node)->textContent;
}
0 голосов
/ 05 августа 2010

По аналогии с XSLT Алехандро 1.0 ", но любые другие идеи тоже могут помочь " ответить ...

XML:

<?xml version="1.0" encoding="UTF-8"?>
<div>
    <span class="url">
        word
        <b class=" ">test</b>
    </span>
    <span class="url">
        word
        <b class=" ">test2</b>
        more words
    </span>
</div>

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="span">
        <xsl:value-of select="normalize-space(data(.))"/>
    </xsl:template>
</xsl:stylesheet>

ВЫВОД:

word test
word test2 more words
0 голосов
/ 05 августа 2010

Как выбрать содержимое строки следующих узлов:

Во-первых, я думаю, что ваш вопрос неясен.

Вы можете выбрать нисходящие текстовые узлы, как Джон Кугельман имеет ответ с

//span//text()

Я рекомендую использовать абсолютный путь (не начиная с //)

Но сдля этого вам нужно будет обработать текстовые узлы, находя у родителей span которые являются дочерними.Поэтому было бы лучше просто выбрать элементы span (например, //span) и затем обработать их строковое значение.

С XPath 2.0 вы можете использовать:

string-join(//span, '.')

Результат:

word test. word test2 more words

С XSLT 1.0 этот вход:

<div>
<span class="url">
 word
 <b class=" ">test</b>
</span>

<span class="url">
 word
 <b class=" ">test2</b>
 more words
</span>
</div>

С этой таблицей стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="span[@class='url']">
        <xsl:value-of select="concat(substring('.',1,position()-1),normalize-space(.))"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

word test.word test2 more words
0 голосов
/ 04 августа 2010
//span//text()

Это может быть лучшее, что вы можете сделать.Вы получите несколько текстовых узлов, потому что текст хранится в отдельных узлах в DOM.Если вам нужна единственная строка, вам нужно просто объединить текстовые узлы самостоятельно, поскольку я не могу придумать, как заставить встроенные функции XPath сделать это.

Использование string() или concat() не будет работать, потому что эти функции ожидают строковые аргументы.Когда вы передаете набор узлов в функцию, ожидающую строку, набор узлов преобразуется в строку, беря текстовое содержимое первого узла в наборе узлов.Остальные узлы отбрасываются.

...