【问题标题】:Extract img src from a text element in an XML feed从 XML 提要中的文本元素中提取 img src
【发布时间】:2021-07-16 09:09:40
【问题描述】:

我有一个如下所示的 XML 提要:

<?xml version="1.0" encoding="UTF-8"?>
<smf:xml-feed xmlns:smf="http://www.simplemachines.org/" xmlns="http://www.simplemachines.org/xml/recent" xml:lang="en-US">
  <recent-post>
    <time>April 04, 2021, 04:20:47 pm</time>
    <id>1909114</id>
    <subject>Title</subject>
    <body><![CDATA[<a href="#"><img src="image.png">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iure rerum in tempore sit ducimus doloribus quod commodi eligendi ipsam porro non fugiat nisi eaque delectus harum aspernatur recusandae incidunt quasi.</a>]]></body>
  </recent-post>
</smf:xml-feed>

我想从body 中提取图像src,然后将其保存到包含image 元素的新XML 文件中。

到目前为止,我已经

$xml = 'https://example.com/feed.xml';
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->recover = true;
libxml_use_internal_errors(true);
$dom->loadXML($xml);

$xpath = new DOMXPath( $dom );
$nodes = $xpath->query( 'smf:xml-feed/recent-post/body' );

foreach( $nodes as $node )
{
    $html = new DOMDocument();
    $html->loadHTML( $node->nodeValue );
    $src = $html->getElementsByTagName( 'img' )->item(0)->getAttribute('src');
    echo $src;
}

但是当我尝试打印出$nodes 时,我什么也得不到。我错过了什么?

【问题讨论】:

    标签: php xml domdocument domxpath


    【解决方案1】:

    这看起来像一个简单的机器提要。但是,名称空间丢失了,“body”元素应该是一个 CDATA 部分,其中包含一个 html 片段作为文本。我希望看起来像这样:

    <smf:xml-feed 
      xmlns:smf="http://www.simplemachines.org/" 
      xmlns="http://www.simplemachines.org/xml/recent" 
      xml:lang="en-US">
        <recent-post>
        <time>April 04, 2021, 04:20:47 pm</time>
        <id>1909114</id>
        <subject>Title</subject>
        <body><![CDATA[
        <a href="#"><img src="image.png">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Iure rerum in tempore sit ducimus doloribus quod commodi eligendi ipsam porro non fugiat nisi eaque delectus harum aspernatur recusandae incidunt quasi.</a>
        ]]>
        </body>
      </recent-post>
    </smf:xml-feed>
    

    XML 定义了两个命名空间。要在 Xpath 表达式中使用它们,您必须为它们注册前缀。我建议迭代 recent-post 元素。然后使用带有字符串类型转换的表达式获取特定子节点的文本内容。

    body 元素包含 HTML 片段作为文本,因此您需要将其加载到单独的文档中。然后你可以在这个文档上用Xpath去获取srcimg

    $feedDocument = new DOMDocument();
    $feedDocument->preserveWhiteSpace = false;
    $feedDocument->loadXML($xmlString);
    $feedXpath = new DOMXPath($feedDocument);
    
    // register namespaces
    $feedXpath->registerNamespace('smf', 'http://www.simplemachines.org/');
    $feedXpath->registerNamespace('recent', 'http://www.simplemachines.org/xml/recent');
    
    // iterate the posts
    foreach($feedXpath->evaluate('/smf:xml-feed/recent:recent-post') as $post) {
        // demo: fetch post subject as string
        var_dump($feedXpath->evaluate('string(recent:subject)', $post));
        
        // create a document for the HTML fragment
        $html = new DOMDocument();
        $html->loadHTML(
            // load the text content of the body element
            $feedXpath->evaluate('string(recent:body)', $post),
            // just a fragment, no need for html document elements or DTD
            LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
        );
        // Xpath instance for the html document
        $htmlXpath = new DOMXpath($html);
        // fetch first src attribute of an img 
        $src = $htmlXpath->evaluate('string(//img/@src)');
        var_dump($src);
    }
    

    输出:

    string(5) "Title"
    string(9) "image.png"
    

    【讨论】:

    • 抱歉,我在浏览器选项卡中查看了提要并最初从那里复制了代码,但它不包含任何命名空间或 CDATA 信息。在查看了页面的源代码后,我能够看到这一点,并且我已经更新了帖子中的代码。
    【解决方案2】:

    你的代码有几个问题,我必须对一些问题做出假设......

    $dom->loadXML($xml);
    

    这是实际的源 XML 而不是 URL,您需要改用 load()

    我不得不假设 smf 命名空间是在文档中的某处定义的,出于测试目的,我已将示例 XML 更改为...

    <smf:xml-feed xml:lang="en-US" xmlns:smf="http://a.com">
    

    我还将查询更改为

    //smf:xml-feed/recent-post/body
    

    测试这段代码。

    最后,不知道为什么要在循环中创建另一个文档,但您应该能够直接从循环中的节点处理它,所以我使用$node 作为getElementsByTagName() 调用的基础...

    $xml = 'https://example.com/feed.xml';
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;
    $dom->recover = true;
    libxml_use_internal_errors(true);
    $dom->load($xml);
    
    $xpath = new DOMXPath( $dom );
    $nodes = $xpath->query( '//smf:xml-feed/recent-post/body' );
    
    foreach( $nodes as $node )
    {
        $src = $node->getElementsByTagName( 'img' )->item(0)->getAttribute('src');
        echo $src;
    }
    

    【讨论】:

    • 另外 - HTML 的 sn-p 中的 img 标签也需要注意 - 作为自关闭元素,它需要 / 关闭元素,否则会导致解析器出错.
    • 对不起,我最初发布提要代码时的错误。请参阅上面我对 ThW 的评论。