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

问题描述

无论元素类型是什么,我都试图用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>"

如何返回类似的内容

<p>text 1</p>
<ol><li>item 1</li><li>item 2</li></ol>
<ul><li>item 1</li></ul>
<h2>header</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")
,

只需回答您所写的问题:

如何遍历所有元素

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

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

鉴于“此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>"

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","h2"]