【问题标题】:Extract img src from cdata text in XML从 XML 中的 cdata 文本中提取 img src
【发布时间】:2020-03-11 01:08:18
【问题描述】:

我想从 XML 文件中提取 img src 值。

测试输入:

<ROOT>
   <ITEM>
      <DESCRIPTION><![CDATA[<p align="left" dir="ltr">
    <span lang="EN">lorem ipsum</span></p>
<p>
    some text</p>
<p>
    <img alt="" src="https://example.com/hello.jpg" /></p>
]]></DESCRIPTION>
    </ITEM>
</ROOT>         

最好的方法是什么?使用 XSLT 或 XML 解析器,例如 xmllint?

目前我正在尝试使用 xmllint:

xmllint --xpath '//ROOT/ITEM/DESCRIPTION/text()' input.xml | egrep -o 'src=".*(\.png|\.jpg)'

...但输出是这样的:

src="https://example.com/hello.jpg

当然,我可以使用 sed 之类的工具删除 src=",但也许有更好、更清洁的解决方案来提取链接?

【问题讨论】:

    标签: xslt xml-parsing xmllint


    【解决方案1】:

    您需要深入挖掘 XPath 3 或 XSLT 3 中的 parse-xml-fragment

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">    
    
      <xsl:output method="text" indent="yes" html-version="5"/>
    
      <xsl:template match="/">
         <xsl:value-of select="ROOT/ITEM/DESCRIPTION/parse-xml-fragment(.)//img/@src"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/3NSSEv7

    Saxon 9.9 HE 提供 .NET、Java 和 C/C++/Python 版本,以运行/使用 XSLT 3。

    如果 CDATA 包含的 HTML 不是格式良好的 X(HT)ML,那么您可以使用 David Carlisle 在 XSLT 2 (https://github.com/davidcarlisle/web-xslt/blob/master/htmlparse/htmlparse.xsl) 中实现的 HTML 解析器:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:html-parser="data:,dpc"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:import href="https://github.com/davidcarlisle/web-xslt/raw/master/htmlparse/htmlparse.xsl"/>
    
      <xsl:output method="text"/>
    
      <xsl:template match="/">
         <xsl:value-of select="ROOT/ITEM/DESCRIPTION/html-parser:htmlparse(., '', true())//img/@src"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/3NSSEv7/1

    【讨论】:

    • 如果我在示例 XML 上运行此 XSLT,它可以工作,但如果我在实际 XML 上运行它,我会收到错误:error at char 46 in xsl:value-of/@select on line 11 column 88 of stage-2.xslt: FODC0006: First argument to parse-xml-fragment() is not a well-formed and namespace-well-formed XML fragment. XML parser reported: org.xml.sax.SAXParseException; systemId: file:./stage-2.xslt; lineNumber: 2;
    • columnNumber: 176; The entity "ndash" was referenced, but not declared. First argument to parse-xml-fragment() is not a well-formed and namespace-well-formed XML fragment. XML parser reported: org.xml.sax.SAXParseException; systemId: file:./stage-2.xslt; lineNumber: 2; columnNumber: 176; The entity "ndash" was referenced, but not declared.
    • 很奇怪,我正在运行 Saxonica 的 Saxon-HE 9.8.0.8J。输入 XML 没有问题,至少如果我在浏览器中打开它,我不会收到任何错误。
    • 好吧,我们无法针对您未在问题中显示的输入编写代码,但错误消息清楚地表明我尝试使用 parse-xml-fragment 解析的 CDATA 部分内容不是一个好 -形成的片段,因为它似乎引用了像ndash 这样的实体,HTML 解析器知道但 XML 解析器不知道,除非有 DTD。所以parse-xml-fragment 无法解析该内容,您可能需要一个 HTML 解析器来将该 CDATA 部分内容提供给,Saxon PE 和 EE 对此有扩展,他没有。您可以使用在纯 XSLT 2 中完成的 David Carlisle 的 HTML 解析器。
    • xsltfiddle.liberty-development.net/3NSSEv7/2 有一个示例,其中包含 HTML 解析器似乎可以很好解析的实体引用。
    【解决方案2】:

    如果您的 CDATA 部分不在 CDATA 部分中,那么它是格式正确的 XML,您可以将 xmllint 的输出通过管道传输到 xmllint,以便将 CDATA 解析为 XML。

    在您的具体示例中,您必须将输出包装在另一个元素中(例如x)以使其格式正确。

    示例...

    xmllint --xpath 'concat("<x>",string(//ROOT/ITEM/DESCRIPTION),"</x>")' input.xml | xmllint --xpath 'string(//img/@src)' -
    

    输出...

    https://example.com/hello.jpg
    

    【讨论】:

    • @BélaNem - 是的,如果有多个 img 元素,它只会输出第一个的 src 值(因为 xmllint 使用 xpath 1.0)。您可能会切换到 xmlstarlet 并遍历每个 img。以后有时间,我再补充一个例子。
    猜你喜欢
    • 2012-04-22
    • 2021-07-16
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多