使用 Nokogiri 迭代 HTML 的块,无论元素类型如何?

Iterate a block of HTML, regardless of element type, with Nokogiri?

我正在尝试使用 Nokogiri 迭代 HTML 的块,无论元素类型是什么。

例如,给定此变量 html,通过 Nokogiri 传递:

require 'nokogiri'

html = "<p>Some text</p><ol><li>List item 1</li><li>List item 2</li></ol><p>Last bit of text</p>"

parsed_html = Nokogiri::HTML(html)

我知道我可以通过以下方式遍历每个 <p>

parsed_html.css("p").each do |p|
  puts p
end

但同样只抓取所有 <p> 标签,而不抓取 <ol> 及其子标签。

我也知道我可以通过以下方式获取 <ol>

parsed_html.css("p, ol").each do |p|
  puts p
end

但是 我如何遍历所有元素而不用明确说明我想遍历哪些元素?

例如,给定另一个 html 块:

html = "<p>text 1</p><ol><li>item 1</li><li>item 2</li></ol><ul><li>item 1</li></ul><h2>header</h2>"

我怎样才能 return 像这样:

<p>text 1</p>
<ol><li>item 1</li><li>item 2</li></ol>
<ul><li>item 1</li></ul>
<h2>header</h2>

提前致谢。

只是回答你写的问题:

如何遍历所有元素

CSS 接受通配符,因此您可以:

Nokogiri::HTML(html).css("*").map(&:name)
# => ["html", "body", "p", "ol", "li", "li", "p"]

给定“这个html”我如何return“类似”

html = "<p>text 1</p><ol><li>item 1</li><li>item 2</li></ol><ul><li>item 1</li></ul><h2>header</h2>"

puts Nokogiri::HTML(html).css('body').inner_html
# <p>text 1</p>
# <ol>
# <li>item 1</li>
# <li>item 2</li>
# </ol>
# <ul><li>item 1</li></ul>
# <h2>header</h2>

我希望能够遍历所有一级子元素(p,ol,ul,h2)

Nokogiri::HTML(html).css('body').children.map(&:name)
# => ["p", "ol", "ul", "h2"]

使用CSS child selector:

parsed_html.css('body > *')

这仅选择元素的直接子元素。

irb(main):015:0> parsed_html = Nokogiri::HTML(html)
irb(main):016:0> parsed_html.css('body > *')
=> [#<Nokogiri::XML::Element:0x3c00 name="p" children=[#<Nokogiri::XML::Text:0x3bec "text 1">]>, #<Nokogiri::XML::Element:0x3c64 name="ol" children=[#<Nokogiri::XML::Element:0x3c28 name="li" children=[#<Nokogiri::XML::Text:0x3c14 "item 1">]>, #<Nokogiri::XML::Element:0x3c50 name="li" children=[#<Nokogiri::XML::Text:0x3c3c "item 2">]>]>, #<Nokogiri::XML::Element:0x3ca0 name="ul" children=[#<Nokogiri::XML::Element:0x3c8c name="li" children=[#<Nokogiri::XML::Text:0x3c78 "item 1">]>]>, #<Nokogiri::XML::Element:0x3cc8 name="h2" children=[#<Nokogiri::XML::Text:0x3cb4 "header">]>]
irb(main):017:0> parsed_html.css('body > *').map {|e| e.name }
=> ["p", "ol", "ul", "h2"]

这是可行的,因为当您使用 Nokogiri::HTML:

时,Nokogiri 会创建一个骨架
irb(main):018:0> parsed_html.to_s
=> "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body>\n<p>text 1</p>\n<ol>\n<li>item 1</li>\n<li>item 2</li>\n</ol>\n<ul><li>item 1</li></ul>\n<h2>header</h2>\n</body></html>\n"

您也可以只使用 Nokogiri::HTML.fragment 而不是 HTML():

frag = Nokogiri::HTML.fragment(html)
frag.children.map(&:to_html).join("\n")