从包含<的文本中剥离HTML和>丝瓜和Nokogiri的人物

时间:2011-06-10 15:13:14

标签: html ruby nokogiri

我想这很常见,这是一个已经解决的问题,但是对于Loofah和Nokogiri而言,我还没有找到解决方案。

我正在使用Loofah,一个包装Nokogiri的HTML scrubber库,用于擦除一些HTML文本以供显示。但是,该文本有时会发生在诸如<>字符之间的电子邮件地址之类的内容中,例如< foo@domain.com >。 Loofah正在考虑将其作为HTML或XML标签,并将其从文本中删除。

有没有办法防止这种情况发生,同时还要做好擦除实际标签的工作?

编辑:这是一个失败的测试用例:

require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'nokogiri'

MAGICAL_REGEXP = /<([^(?:\/|!\-\-)].*)>/

def filter_html(content)
  # Current approach in a gist: We capture content enclosed in angle brackets.
  # Then, we check if the excerpt right after the opening bracket is a valid HTML
  # tag. If it's not, we substitute the matched content (which is the captured
  # content enclosed in angle brackets) for the captured content enclosed in
  # the HTML entities for the angle brackets. This does not work with nested
  # HTML tags, since regular expressions are not meant for this.

  content.to_s.gsub(MAGICAL_REGEXP) do |excerpt|
    capture = $1
    Nokogiri::HTML::ElementDescription[capture.split(/[<> ]/).first] ? excerpt : "&lt;#{capture}&gt;"
  end
end

class HTMLTest < Test::Unit::TestCase
  def setup
    @raw_html = <<-EOS
<html>
<foo@bar.baz>
<p><foo@<b class="highlight">bar</b>.baz></p>
<p>
<foo@<b class="highlight">bar</b>.baz>
</p>
< don't erase this >
</html>
EOS

    @filtered_html = <<-EOS
<html>
&lt;foo@bar.baz&gt;
<p>&lt;foo@<b class="highlight">bar</b>.baz&gt;</p>
<p>
&lt;foo@<b class="highlight">bar</b>.baz&gt;
</p>
&lt; don't erase this &gt;
</html>
EOS
  end

  def test_filter_html
    assert_equal(@filtered_html, filter_html(@raw_html))
  end
end

# Can you make this test pass?
Test::Unit::UI::Console::TestRunner.run(HTMLTest)

我们目前正在使用一些非常邪恶的正则表达式hackery来尝试实现这一目标,但正如上面的评论所述,它不适用于非嵌套内部标签“嵌套”。我们实际上也希望保留<b class="highlight">元素。

以下示例不使用Loofah,但应用程序本身也在其他地方使用,因此在此处添加它并不困难。我们只是不确定应该使用哪些配置选项,如果有的话。

1 个答案:

答案 0 :(得分:2)

由于主要问题是HTML实体尖括号中包含的HTML标签 - 完全被Nokogiri破坏 - 我们通过删除上述HTML标签,转义非HTML标签尖括号然后放入HTML标签回来了。这听起来有点hackish,但它的工作完美。我们的第一个目标是转发尖括号中的电子邮件地址,但这种方法(据称)适用于任何类型的文本。

# Does not run on ruby 1.9

require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'nokogiri'
require 'active_support/secure_random'

def filter_html(content)
  # Used to mark highlighted words.
  random_hex = SecureRandom.hex(6)

  # Remove highlighting.
  highlighted_terms = []
  without_highlighting = content.to_s.gsub(/<b class="highlight">(.*?)<\/b>/) do |match|
    highlighted_terms << $1
    "highlight-#{random_hex}:#{$1}"
  end

  # Escape non-HTML angle brackets.
  escaped_content = without_highlighting.to_s.gsub(/<(?:\s*\/)?([^!\-\-].*?)>/) do |excerpt|
    capture = $1
    tag = capture.split(/[^a-zA-Z1-6]/).reject(&:empty?).first
    !!Nokogiri::HTML::ElementDescription[tag] ? excerpt : "&lt;#{capture}&gt;"
  end

  # Add highlighting back.
  highlighted_terms.uniq.each do |term|
    escaped_content.gsub!(/highlight-#{random_hex}:(#{term})/) do |match|
      "<b class=\"highlight\">#{$1}</b>"
    end
  end

  escaped_content
end

class HTMLTest < Test::Unit::TestCase
  def setup
    @raw_html = <<-EOS
      <html>
        <foo@bar.baz>
        <p><foo@<b class="highlight">bar</b>.baz></p>
        <p>
          <foo@<b class="highlight">bar</b>.baz>
        </p>
        <    don't erase this   >
      </html>
    EOS

    @filtered_html = <<-EOS
      <html>
        &lt;foo@bar.baz&gt;
        <p>&lt;foo@<b class="highlight">bar</b>.baz&gt;</p>
        <p>
          &lt;foo@<b class="highlight">bar</b>.baz&gt;
        </p>
        &lt;    don't erase this   &gt;
      </html>
    EOS
  end

  def test_filter_html
    assert_equal(@filtered_html, filter_html(@raw_html))
  end
end

# It passes!
Test::Unit::UI::Console::TestRunner.run(HTMLTest)