【问题标题】:How to remove all tags except for some using Nokogiri如何使用 Nokogiri 删除除某些标签外的所有标签
【发布时间】:2011-06-20 14:17:18
【问题描述】:

除了使用 Nokogiri 的某些元素外,如何删除某个节点下的所有标签? 例如,使用此设置:

src = <<EOS
<html>
  <body>
    <p>
      Hello <i>world</i>!
      This is <em>another</em> line.
      <p><h3>And a paragraph <em>with</em> a heading.</h3></p>
      <b>Third line.</b>
    </p>
  </body>
</html>
EOS

doc = Nokogiri::HTML(src)    
para = doc.at('//p')

如何删除段落中除 元素之外的所有元素(同时保留其内容)? 所以结果是:

<html>
  <body>
    <p>
      Hello <i>world</i>!
      This is another line.
      And a paragraph with a heading.
      <b>Third line.</b>
    </p>
  </body>
</html>

【问题讨论】:

  • 这更像是一个合适的 XSLT 任务。
  • Nokogiri 也支持 XSLT,所以一个 XSLT 示例就可以了。
  • 谢谢,我试试看。

标签: ruby xslt nokogiri


【解决方案1】:

Flack 使用 XSLT 模板给出了正确答案,我在这里提供了一个完整的基于 Nokogiri 的示例:

xslt = <<EOS
<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
    <output method="html" indent="yes"/>

    <template match="node() | @*">
        <copy>
            <apply-templates select="node() | @*"/>
        </copy>
    </template>

    <template match="p//*[not(self::i or self::b)]">
        <apply-templates/>
    </template>
</stylesheet>
EOS

src = <<EOS
<html>
  <body>
    <p>
      Hello <i>world</i>!
      This is <em>another</em> line.
      <p><h3>And a paragraph <em>with</em> a heading.</h3></p>
      <b>Third line.</b>
    </p>
  </body>
</html>
EOS

doc       = Nokogiri::XML(src)
paragraph = doc.at('p')

xslt = Nokogiri::XSLT(xslt)
transformed_paragraph = xslt.transform(paragraph)
paragraph.replace transformed_paragraph.children

puts doc 

输出:

<?xml version="1.0"?>
<html>
  <body>
    <p>
      Hello <i>world</i>!
      This is another line.
      And a paragraph with a heading.
      <b>Third line.</b>
    </p>
  </body>
</html>

【讨论】:

  • 请添加生成的输出。
【解决方案2】:

只是为了完善示例,这里有一个使用 Nokogiri 而没有 XSLT:

require 'nokogiri'

src = <<EOS
<html>
  <body>
    <p>
      Hello <i>world</i>!
      This is <em>another</em> line.
      <p><h3>And a paragraph <em>with</em> a heading.</h3></p>
      <b>Third line.</b>
    </p>
  </body>
</html>
EOS

doc = Nokogiri::HTML(src)

if (doc.errors.any?)
  puts "doc.errors:"
  doc.errors.each do |e|
    puts "#{ e.line }: #{ e.to_s }"
  end
  puts
end

doc.search('//p/*').each do |n| 
  n.replace(n.content) unless (%w[i b].include?(n.name))
end

puts doc.to_html
# >> doc.errors:
# >> 6: Unexpected end tag : p
# >> 8: Unexpected end tag : p
# >> 
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html><body>
# >>     <p>
# >>       Hello <i>world</i>!
# >>       This is another line.
# >>       </p>
# >> <p></p>
# >> <h3>And a paragraph <em>with</em> a heading.</h3>
# >>       <b>Third line.</b>
# >>     
# >>   </body></html>

请注意,Nokogiri 对标记不满意并进行了一些修复。而且,去除标签的实际代码只有三行,可以写在一行上。

【讨论】:

  • XPath 变体:doc.xpath('//p/*[not(name()="i" or name()="b")]')
  • XPath 表达式只选择直接子级,但嵌套元素是 (X)HTML 的经典内容...递归中存在复杂性,以及 XSLT 答案的原因。
【解决方案3】:
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="em | p/p | h3">
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

应用于您的样本,结果将是:

<html>
    <body>
        <p>
      Hello 
            <i>world</i>!
      This is another line.
      And a paragraph with a heading.
            <b>Third line.</b>
        </p>
    </body>
</html>

根据 cmets 的要求进行编辑。

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="p//*[not(self::i or self::b)]">
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

这将删除 p 中的所有元素(标记,而不是字符串值),ib 元素除外。

【讨论】:

  • 我试过了。它适用于这个特定的例子。但我正在寻找一种方法来删除 all 标签,除了一些标签。这只会从我的示例中删除 em、p/p 和 h3。任何想法如何做到这一点?我对 XSLT 不太熟悉(我尝试了类似 *[not(name()='i')][not(name()='b')] 的匹配,但这不会产生预期的结果)
  • 这需要展示如何将 Nokogiri 与 XSLT 一起使用。更高级的 Nokogiri 用户可以弄清楚,但这不是 SO 的意义所在。
  • @锡人。这就是为什么我只在 OP 说“一个 XSLT 示例就可以”之后才发布。
  • 是的,我明白这一点,但这样做也是为了帮助后来出现的其他人,因为搜索显示此页面上有一些有用的东西。
  • @the Tin Man:那些拥有 Ruby 环境但没有 Nokogiri 的人呢?安装 Nokogiri 是答案的一部分吗?那么,如何使用 Nokogiri 运行 XSLT 转换是一个不同的问题。
猜你喜欢
  • 1970-01-01
  • 2013-05-01
  • 1970-01-01
  • 2013-04-28
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 2014-12-01
  • 1970-01-01
相关资源
最近更新 更多