从 XML 提要中的文本元素中提取 img src

Extract img src from a text element in an XML feed

我的 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,然后将其保存到新的 XML 文件中,其中包含 image.[=17 的元素=]

到目前为止,我有

$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 时,我什么也没得到。我错过了什么?

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

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

这看起来像简单机器提要。但是缺少命名空间,“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来获取imgsrc:

$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"