Как удалить все теги, кроме некоторых, использующих Nokogiri - PullRequest
2 голосов
/ 03 февраля 2011

Как удалить все теги ниже определенного узла, кроме некоторых элементов, использующих 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>

Ответы [ 3 ]

4 голосов
/ 03 февраля 2011

Просто, чтобы завершить примеры, вот один, использующий 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 не доволен разметкой и сделал некоторые исправления.И что фактический код для удаления тегов был всего тремя строками и мог быть записан в одну.

3 голосов
/ 03 февраля 2011

Флэк дал правильный ответ, используя шаблон XSLT, я приведу полный пример на основе Нокогири:

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>
0 голосов
/ 03 февраля 2011
<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>

РЕДАКТИРОВАТЬ согласно запросу в комментариях.

<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, за исключением i и b элементов.

...