【问题标题】:How do I search and replace nodes Nokogiri?如何搜索和替换节点 Nokogiri?
【发布时间】:2013-07-08 04:42:05
【问题描述】:

我有一个 HTML 文档作为字符串。我使用 Nokogiri 解析它:

doc_str = <<-mydoc
  <p>Lorem ipsum dolor sit foo.</p>
  <h2>Consectetur adipisicing bar</h2>
  <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p>
mydoc

doc = Nokogiri::HTML doc_str

我想在所有可见文本中将 "foo" / "Foo" 替换为 "Bar" / "bar"

desired = <<-mydoc
  <p>Lorem ipsum dolor sit bar.</p>
  <h2>Consectetur adipisicing bar</h2>
  <p>Bar do <a href="/c-foo.aspx" class = "foo" title="Bar bar.">foofoo bar</a>.</p>
mydoc

我该怎么做?

我试图阅读Nokogiri tutorial,它描述了Nokogiri::HTML::Document#at_css。使用 Ruby 2.0 和最新的 Nokogiri,doc.at_css 'h1' 返回nil,所以h1.content = "something" 甚至是不可能的。

即使它有效,也只是解决我的查找和替换问题的第一步。

【问题讨论】:

    标签: ruby nokogiri


    【解决方案1】:

    doc.at_css 'h1' 返回 nil,因为您的 HTML 中没有 h1 元素。 doc.at_css 'h2' 正确返回 Nokogiri::XML::Element 对象的 h2 元素。

    CSS 选择器无法选择文本节点,并且对于这类事情来说是一个糟糕的工具。 XPath 将完成 CSS 所做的所有事情以及更多其他事情。文档根目录下任意位置的文本节点就是//text()

    编辑我刚刚注意到您似乎希望属性的内容以相同的方式更改。 @* 匹配任何属性,因此 XPath 表达式变为 //@* | //text()。虽然我不清楚这一点,因为href="/c-foo.aspx"class="foo" 保持不变,但title="Foo bar." 变为title="bar bar."。我相信你可以自己解决这个问题。

    您需要使用 XPath 找到所有文本节点,然后使用 content 获取每个节点的文本值。随意修改,用content=替换。

    这个程序演示。 to_html 方法将数据包装在必要的标签中,使其成为有效的 HTML。

    require 'nokogiri'
    
    doc_str = <<-HTML
      <p>Lorem ipsum dolor sit foo.</p>
      <h2>Consectetur adipisicing bar</h2>
      <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p>
    HTML
    
    doc = Nokogiri::HTML(doc_str)
    
    doc.xpath('//@*', '//text()').each do |node|
      node.content = node.content.gsub(/\bfoo\b/, 'bar').gsub(/\bFoo\b/, 'Bar')
    end
    
    puts doc.to_html
    

    输出

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
    <html><body>
    <p>Lorem ipsum dolor sit bar.</p>
      <h2>Consectetur adipisicing bar</h2>
      <p>Bar do <a href="/c-bar.aspx" class="bar" title="Bar bar.">foofoo bar</a>.</p>
    </body></html>
    

    【讨论】:

    • +1 教了我这么多,但请注意,您的示例并不完全正确,因为它也将 class="foo" 替换为 class="bar",这是不应该的,因为它不是可见文字。相反,@pguardiario 的#traverse 示例没有遇到class="bar" 的问题,他错过了\b 环绕fooFoo。你能告诉我更多关于#xpath方法的论点吗?我的意思是'//@* | //text()'。天堂是什么意思?
    • @BorisStitnicky:所以您问题中的属性之间的差异是故意的?这一点都不清楚。您必须枚举要处理的属性的名称才能正确处理。 XPath 术语text()@* 我已经解释过了。 // 表示“后代”,当它位于表达式的开头时,表示“根的后代”,即文档中的任何位置。管道| 是“联合”,因此完整的表达式选择任何位置的所有属性节点和所有文本节点。 Look here for the specification of XPath 1.0.
    • @* 更改为@title,我想你会成功的。
    • @MarkThomas:是的,我可以把它放进去,但它只能在 this 示例中使用。如果要进行更改,那么我们需要一个要转换的所有属性的名称列表。
    • 是的,OP 需要定义一个可见属性列表。没有自动的方法可以做到这一点。但是替换 all 属性是不正确的。
    猜你喜欢
    • 1970-01-01
    • 2021-04-20
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    相关资源
    最近更新 更多