如何删除HTML节点之间的空格?

时间:2013-05-07 10:56:44

标签: ruby-on-rails ruby ruby-on-rails-3 nokogiri

我正在尝试从<p>标记

之间的HTML片段中删除空格
<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>

如您所见,<p> </p>标记之间总是有空格。

问题是在将字符串保存到我的数据库时,空格会创建<br>标记。 像stripgsub这样的方法只删除节点中的空格,从而导致:

<p>FooBar</p> <p>barbarbar</p> <p>bla</p>

虽然我想:

<p>Foo Bar</p><p>bar bar bar</p><p>bla</p>

我正在使用:

  • Nokogiri 1.5.6
  • Ruby 1.9.3
  • Rails

更新

偶尔会有<p>个标签的子节点产生同样的问题:

之间的空格

示例代码

注意:代码通常在一行中,我重新格式化它,因为否则会无法忍受......

<p>
  <p>
    <strong>Selling an Appartment</strong>
  </p>
  <ul>
    <li>
      <p>beautiful apartment!</p>
    </li>
    <li>
      <p>near the train station</p>
    </li>
    .
    .
    .
  </ul>
  <ul>
    <li> 
      <p>10 minutes away from a shopping mall </p>
    </li>
    <li>
      <p>nice view</p>
    </li>
  </ul>
  .
  .
  .
</p>

我如何剥去那些白色空间呢?

事实证明,我使用gsub方法搞砸了,并没有进一步调查gsub使用regex的可能性......

简单的解决方案是添加

data = data.gsub(/>\s+</, "><")

它删除了所有不同节点之间的空格...正则表达式!

4 个答案:

答案 0 :(得分:1)

这就是我编写代码的方式:

require 'nokogiri'

doc = Nokogiri::HTML::DocumentFragment.parse(<<EOT)
<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>
EOT

doc.search('p, ul, li').each { |node| 
  next_node = node.next_sibling
  next_node.remove if next_node && next_node.text.strip == ''
}

puts doc.to_html

结果是:

<p>Foo Bar</p><p>bar bar bar</p><p>bla</p>

打破它:

doc.search('p')

仅查找文档中的<p>个节点。 Nokogiri从search返回NodeSet,如果没有匹配则返回nil。代码循环遍历NodeSet,依次查看每个节点。

next_node = node.next_sibling

获取指向当前<p>节点之后的下一个节点的指针。

next_node.remove if next_node && next_node.text.strip == ''
如果下一个节点不是nil并且在剥离时其文本不为空,则

next_node.remove从DOM中删除当前next_node,换句话说,如果节点只有空格。

如果所有文本节点都应从文档中删除,还有其他技术可以仅定位TextNodes。这是有风险的,因为它最终会删除标签之间的所有空白,导致连续的句子和加入的单词,这可能不是你想要的。

答案 1 :(得分:0)

第一个解决方案可以是删除空文本节点,为您的确切情况快速执行此操作可以是:

require 'nokogiri'
doc = Nokogiri::HTML("<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>")
doc.css('body').first.children.map{|node| node.to_s.strip}.compact.join

这对于嵌套元素不会起作用,但应该为你提供一条良好的开端。

更新:

您实际上可以通过以下方式进行优化:

require 'nokogiri'
doc = Nokogiri::HTML::DocumentFragment.parse("<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>")
doc.children.map{|node| node.to_s.strip}.compact.join

答案 2 :(得分:0)

这里是您可以寻找的所有可能的任务,这些任务在解析输出时处理不必要的空格(包括 unicode 空格)。

html = "<p>A paragraph.<em>&nbsp; &nbsp;</em> <br><br><em>&nbsp; &nbsp; &nbsp; 
</em></p><p><em>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </em>
</p><p><em>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 
&nbsp; &nbsp;&nbsp; </em><strong><em>\" Quoted Text \"&nbsp; </em></strong></p>
<ul><li><p>List 1</p></li><li><p>List 2</p></li><li><p>List 3 </p>
<p><br></p><p><br><em> &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</em><br>
A text content.<br><em><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </em></p></li></ul>"

doc = Nokogiri::HTML.fragment(html)

doc.traverse { |node|
  # removes any whitespace node
  node.remove if node.text.gsub(/[[:space:]]/, '') == ''

  # replace mutiple consecutive spaces with single space
  node.content = node.text.gsub(/[[:space:]]{2,}/, ' ') if node.text?
}

# Gives you html without any text node including <br> or multiple spaces anywhere in the text of html
puts doc.to_html

# Gives text of html, concatenating li items with a space between them
# By default li items text are concatenated without the space     
Nokogiri::HTML(doc.to_html).xpath('//text()').map(&:text).join(' ')

#Output 
# "A paragraph. \" Quoted Text \"  \n List 1 \n List 2 \n \n List 3  \n A text content. \n \n"

# To Remove newline character '\n'
Nokogiri::HTML(doc.to_html).xpath('//text()').map(&:text).join(' ').gsub(/\n+/,'')

#Output
# "A paragraph. \" Quoted Text \"   List 1  List 2   List 3   A text content."

注意:如果您没有在完整的 fragment 文档中使用 html,那么您可能需要将 traverse 替换为其他函数,例如 {{ 1}}。

答案 3 :(得分:-1)

data.squish做同样的事情并且更具可读性。