【问题标题】:XPath Node to StringXPath 节点到字符串
【发布时间】:2011-03-25 11:06:05
【问题描述】:

如何选择以下节点的字符串内容:

<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 个节点

我在 php 中使用 simple_xml,我认为唯一的其他选择是使用 //span 返回:

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

            [b] => test
        )

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

            [b] => test2
        )

)

*请注意,它还会从第二个跨度中删除“更多单词”文本。

所以我想我可以如何使用 php 将数组中的项目展平? Xpath 是首选,但任何其他想法也会有所帮助。

【问题讨论】:

  • 也尝试使用 //span//text() 但这会将文本拆分为 simple_xml 中的单独元素
  • 你需要它带还是不带实际的 b 标签? (您确实需要我收集的内容,但是标签字符串呢)。与DOM 相比,您对SimpleXML 的投入程度如何?
  • 我宁愿没有 b 标签,但如果它们是返回的,它们很容易删除。主要目标是为每个跨度返回 1 个字符串。我不完全理解简单的 xml 对象和我猜的 dom 对象之间的区别。我创建了一个 dom 对象,将 html 加载到其中,然后在 simple_xml 中导入 dom 对象。然后我可以对对象执行 xpath 。返回的是一组简单的 xml 对象(我认为)。这就是我所做的: $html = new DOMDocument(); @$html->loadHTMLFile($url); $xml = simplexml_import_dom($html); //查找所有链接 $result = $xml->xpath("//span");
  • 添加了一个简单的 DOM 示例作为答案。

标签: php xml xslt xpath simplexml


【解决方案1】:

按照 Alejandro 的 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

【讨论】:

  • 谢谢。如果我打算使用 XSL,我很确定这会起作用,但 xpath 示例更适合我正在做的小事。我已经习惯了一些我们在工作中使用的自定义扩展,这些扩展也不在 EXSLT 中。
  • fn:data() 是 XPath 2.0,所以我想你应该说这个解决方案是 XSLT 2.0
【解决方案2】:

你甚至不需要 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-&gt;textContent;而不是替换nodeValue。我知道您希望跨度使用一个字符串,而不是嵌套结构。在这种情况下,您还应该考虑在 span sn-p 上简单地运行 strip_tags 是否不是更快更容易的替代方案。


使用 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;
}

【讨论】:

  • 我不确定这是 OP 所要求的。这样做是打印整个文档,并删除 标记下的所有标记。即第一个 span 元素现在是 &lt;span class="url"&gt;word test&lt;/span&gt; 而不是 &lt;span class="url"&gt;word &lt;b class=" "&gt;test&lt;/b&gt;&lt;/span&gt;
  • @Alexandra 问题下方的 OP 评论为 主要目标是为每个跨度返回 1 个字符串。。我把这解释为替换原来的字符串,但现在你这么说,是的,可能是错的。
  • 是的,我的主要目标是将 span 的内容转换为字符串。简单的 xml 正在获取标签并将它们转换为数组。
  • 嗯,从来没有真正需要 registerPHPFunctions,但在过去它会节省相当多的时间。注意!
  • @Wrikken 我还没有找到对它们的真正需求。主要的缺点是必须写php:function("functioname", ...php:functionString("functioname", ... - 这太麻烦了。然后,您的 XPath 查询将不再可移植到其他语言。但是,由于它是可能的并且它不是一个众所周知的功能,我想我在这里添加它们。 @salathe 在cowburn.info/2009/10/23/php-funcs-xpath 上写了一篇关于此的博客文章
【解决方案3】:

如何选择字符串内容 以下节点:

首先,我认为你的问题不清楚。

您可以选择约翰·库格曼回答的后代文本节点

//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

【讨论】:

  • @Gordon:“但任何其他想法也会有所帮助。”
  • @Alejandro 只是说,以防有人尝试并想知道为什么它不起作用
  • @Gordon:我添加了一个 XPath 2.0 解决方案,因为希望更多人知道它的新功能并更新他们的平台或请求供应商这样做。
【解决方案4】:

使用 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

【讨论】:

    【解决方案5】:

    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;
    }
    

    【讨论】:

    • 有趣。但是像@Wrikken answer 中的所有内容都使用 DOM 会更简单
    • DOM 使用起来比 SimpleXML 复杂一个数量级,但是是的,不管对你有用。
    • 对不起。我并不是说我们应该一直使用 DOM。 DOM 代码会变得非常冗长。但是在这个简单任务的上下文中,我看不到混合这两个 API 的意义。实际上,在这种情况下,您可以通过不调用 dom_import_simplexml() 来节省一些击键
    【解决方案6】:
    $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;
    

    【讨论】:

    • 这就是我要找的。谢谢。
    【解决方案7】:
    //span//text()
    

    这可能是你能做的最好的了。您将获得多个文本节点,因为文本存储在 DOM 中的单独节点中。如果你想要一个字符串,你必须自己连接文本节点,因为我想不出一种方法来让内置的 XPath 函数来做到这一点。

    使用string()concat() 将不起作用,因为这些函数需要字符串参数。当您将节点集传递给需要字符串的函数时,节点集将通过获取节点集中 first 节点的文本内容转换为字符串。其余节点被丢弃。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 2018-02-03
      • 2016-04-08
      • 2011-05-13
      • 2023-04-04
      • 1970-01-01
      • 2011-08-03
      相关资源
      最近更新 更多