在 Rails 中从 HTML 段落创建文本摘录

Create text excerpt from HTML paragraphs in Rails

提问人:sn3p 提问时间:3/7/2022 最后编辑:sn3p 更新时间:3/10/2022 访问量:241

问:

我正在尝试提取一篇文章的摘录(Markdown 解析为 HTML),其中仅包含段落中的纯文本。所有 HTML 都需要被剥离,换行符、制表符和连续空格需要替换为单个空格。

我的第一步是创建一个简单的测试:

describe "#from_html" do
  it "creates an excerpt from given HTML" do
    html = "<p>The spice extends <b>life</b>.<br>The spice    expands consciousness.</p>\n
           <ul><li>Skip me</li></ul>\n
           <p>The <i>spice</i> is vital to space travel.</p>"

    text = "The spice extends life. The spice expands consciousness. The spice is vital to space travel."

    expect(R::ExcerptHelper.from_html(html)).to eq(text)
  end
end

并开始摆弄并想出了这个:

def from_html(html)
  Nokogiri::HTML.parse(html).css("p").map{|node|
    node.children.map{|child|
      child.name == "br" ? child.replace(" ") : child
    } << " "
  }.join.strip.gsub(/\s+/, " ")
end

我在 Rails 上有点生锈,这可能可以更高效、更优雅地完成。我希望在这里得到一些指导。

提前致谢!


方法 2

转向 sanitize 方法(感谢 @max)并编写基于 Rails::Html::P ermitScrubber 的自定义洗涤器


方法 3

意识到我的源文档的格式是 Markdown 的,我冒险探索了一个自定义的 Redcarpet 渲染器。

有关完整示例,请参阅我的答案

html ruby-on-rails ruby html-parsing nokogiri

评论

0赞 max 3/7/2022
api.rubyonrails.org/classes/ActionView/Helpers/......
0赞 max 3/7/2022
其余的文本助手很可能具有您想要的压缩文本和缩短文本的功能。api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html
0赞 sn3p 3/7/2022
@max那将如何运作呢?我知道,但这并不能过滤掉不需要的标签(我只想要段落中的文本)。sanitize
0赞 sn3p 3/7/2022
也替换为 “”,因此我的测试中的第一行缺少一个空格:.<br>... extends life.The spice ...

答:

0赞 sn3p 3/10/2022 #1

我最终编写了一个自定义的红地毯渲染器(灵感来自 Redcarpet::Render::StripDown)。这似乎是最干净的方法,格式之间的解析和转换最少。

module R::Markdown
  class ExcerptRenderer < Redcarpet::Render::Base
    # Methods where the first argument is the text content
    [
      # block-level calls
      :paragraph,

      # span-level calls
      :codespan, :double_emphasis,
      :emphasis, :underline, :raw_html,
      :triple_emphasis, :strikethrough,
      :superscript, :highlight, :quote,

      # footnotes
      :footnotes, :footnote_def, :footnote_ref,

      # low level rendering
      :entity, :normal_text
    ].each do |method|
      define_method method do |*args|
        args.first
      end
    end

    # Methods where content is replaced with an empty space
    [
      :autolink, :block_html
    ].each do |method|
      define_method method do |*|
        " "
      end
    end

    # Methods we are going to [snip]
    [
      :list, :image, :table, :block_code
    ].each do |method|
      define_method method do |*|
        " [#{method}] "
      end
    end

    # Other methods
    def link(link, title, content)
      content
    end

    def header(text, header_level)
      " #{text} "
    end

    def block_quote(quote)
      " “#{quote}” "
    end

    # Replace all whitespace with single space
    def postprocess(document)
      document.gsub(/\s+/, " ").strip
    end
  end
end

并解析它:

extensions = {
  autolink:                     true,
  disable_indented_code_blocks: true,
  fenced_code_blocks:           true,
  lax_spacing:                  true,
  no_intra_emphasis:            true,
  strikethrough:                true,
  superscript:                  true,
  tables:                       true
}

markdown = Redcarpet::Markdown.new(R::Markdown::ExcerptRenderer, extensions)

markdown.render(md).html_safe