Выполнить итерацию блока HTML, независимо от типа элемента, с помощью Nokogiri?

#html #ruby #nokogiri

#HTML #ruby #nokogiri

Вопрос:

Я пытаюсь выполнить итерацию блока HTML с помощью Nokogiri, независимо от типа элемента.

Например, учитывая эту переменную 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>
  

Заранее спасибо.

Комментарии:

1. CSS принимает подстановочные знаки, то есть, как, parsed_html.css("*").map(amp;:name) ?

2. @Kache закрыть, но да, я знаю, что подстановочные знаки работают, но они также возвращают все дочерние элементы (li и т. Д.). Я хочу иметь возможность выполнять итерацию по всем дочерним элементам первого уровня (p, ol, ul, h2) одновременно

3. ах, почему ты не сказал об этом с самого начала? ок обновил мой ответ

Ответ №1:

Используйте дочерний селектор CSS:

 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 создаст скелет при использовании Nokogiri::HTML:

 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(amp;:to_html).join("n")
  

Ответ №2:

Просто отвечаю на вопросы, которые вы написали:

как я могу выполнить итерацию по всем элементам

CSS принимает подстановочные знаки, поэтому вы можете просто:

 Nokogiri::HTML(html).css("*").map(amp;:name)
# => ["html", "body", "p", "ol", "li", "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(amp;:name)
# => ["p", "ol", "ul", "h2"]
  

Комментарии:

1. Мы можем расширить ваш код, чтобы распечатать вложенные дочерние элементы: ruby Nokogiri::HTML(html).css('body').children.map { |node| "<#{node.name}>#{node.children.to_s}<#{node.name}/>" } должен возвращать: ruby [ "<p/>text 1<p/>", "<ol/><li>item 1</li>n<li>item 2</li><ol/>", "<ul/><li>item 1</li><ul/>", "<h2/>header<h2/>" ]