#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/>" ]