Ваш HTML необходимо уменьшить; нет причин иметь более одного дубликата для тестирования кода, который создает уникальные значения.
Кроме того, чтобы сделать более очевидным, что удаляется, я массировал HTML, сортируя узлы для группировки дубликатов.
Я бы начал с этого:
require 'nokogiri'
require 'set'
doc = Nokogiri::HTML(<<EOT)
<body>
<html>
<p><strong><span style="color:#C48189; font-size: 24px;">Enterprise</span></strong></p>
<p><strong><span style="color:#C48189; font-size: 24px;">Enterprise</span></strong></p>
<p><strong><span style="color:#F87217; font-size: 24px;">Government</span></strong></p>
<p><strong><span style="color:#F87217; font-size: 24px;">Government</span></strong></p>
<p><strong><span style="color:blue; font-size: 24px;">Midmarket</span></strong></p>
<p><strong><span style="color:blue; font-size: 24px;">Midmarket</span></strong></p>
<p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
<p><strong><span style="color:green; font-size: 20px;">Car</span></strong><p>
<p><strong><span style="color:red; font-size: 20px;">Mon</span></strong><p>
<p><strong><span style="color:red; font-size: 20px;">Mon</span></strong><p>
<p>888</p>
<p>921</p>
<p>Canada</p>
<p>Canada</p>
<p>France</p>
<p>France</p>
<p>Germany</p>
<p>Germany</p>
</body>
</html>
EOT
Перед запуском HTML содержит:
doc.search('p').size # => 21
Вот как удалить дубликаты:
p_nodes = Set.new
doc.search('p').each { |p|
if p_nodes.include?(p.to_html)
p.remove
else
p_nodes.add(p.to_html)
end
}
После запуска кода HTML содержит:
doc.search('p').size # => 12
В результате:
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><strong><span style="color:#C48189; font-size: 24px;">Enterprise</span></strong></p>
# >>
# >> <p><strong><span style="color:#F87217; font-size: 24px;">Government</span></strong></p>
# >>
# >> <p><strong><span style="color:blue; font-size: 24px;">Midmarket</span></strong></p>
# >>
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p>
# >> </p>
# >> <p><strong><span style="color:red; font-size: 20px;">Mon</span></strong></p>
# >> <p>888</p>
# >> <p>921</p>
# >> <p>Canada</p>
# >>
# >> <p>France</p>
# >>
# >> <p>Germany</p>
# >>
# >>
# >> </body></html>
Set выполняет маги c , Набор похож на Ха sh, только каждый элемент не имеет значения, связанного с ним, это только ключ. И, как Ha sh, набор может содержать только один экземпляр этого элемента, который, в данном случае, является текстовой версией этого конкретного <p>
узла. В результате получается, что каждый узел проверяется на соответствие p_nodes
, чтобы определить, существует ли он, и удаляется, если он есть, в противном случае он добавляется до тех пор, пока не будет проверен каждый узел <p>
.
Примечание : Nokogiri не удаляет узлы Text, содержащие концы строк, которые следуют за узлами <p>
, поэтому есть пробелы. Браузеры "проглатывают пустые места", поэтому полученная отрисовка HTML должна выглядеть одинаково, только без дубликатов.
После запуска вывод показывает:
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p>
# >> </p>
Я думаю, что это результат Нокогири видит, что HTML искажен. Есть три строки, которые не имеют правильных закрывающих тегов </p>
<p><strong><span style="color:green; font-size: 20px;">Car</span></strong><p>
<p><strong><span style="color:red; font-size: 20px;">Mon</span></strong><p>
<p><strong><span style="color:red; font-size: 20px;">Mon</span></strong><p>
, и в процессе тихого исправления HTML добавляется паразитная пара. Это происходит потому, что люди, генерирующие HTML, не обращают внимания на детали.
Эти конкретные узлы являются причиной вышеуказанного пустого узла <p>
и дублированных строк "Cars" выше.
Обычно Nokogiri будет отмечать ошибки разметки, если они существенны, с использованием Nokogiri::XML::Document#errors
метод, но <p>
имеют необязательные закрывающие теги, так что это может быть утечка через трещину.
Исправление искаженных линий не приводит к дублированию "Cars" или пустым <p>
узел, от:
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >> <p>
# >> </p>
до:
# >> <p><strong><span style="color:green; font-size: 20px;">Car</span></strong></p>
# >>
# >> <p><strong><span style="color:red; font-size: 20px;">Mon</span></strong></p>
# >>
Иногда нам необходимо предварительно обработать документы, чтобы исправить их, прежде чем передавать их в Nokogiri, если разметка слишком сильно искажена. Нокогири может делать только так много.
Вот что происходит после того, как парсер исправляет ошибки; Это правильная разметка:
doc = Nokogiri::HTML(<<EOT)
<p>foo</p>
EOT
doc.to_html
# => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n" +
# "<html><body>\n" +
# "<p>foo</p>\n" +
# "</body></html>\n"
, которую Nokogiri не исправляет, по сравнению с:
doc = Nokogiri::HTML(<<EOT)
<p>foo<p>
EOT
doc.to_html
# => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n" +
# "<html><body>\n" +
# "<p>foo</p>\n" +
# "<p>\n" +
# "</p>\n" +
# "</body></html>\n"
Обратите внимание, как Nokogiri пришлось закрыть первый и второй <p>
теги, что привело к постороннему <p></p>
пара, а не в оригинальном документе. Это будет отражено в отрисованном выводе страницы, потому что браузер отобразит его как разрыв абзаца, но вы должны написать код для управления этим, как сказано выше. С того момента, как Нокогири вставил закрывающий </p>
в конец документа, вполне вероятно, что отображение будет отличаться от ожидаемого, следовательно, необходимо предварительно обработать и исправить входящий HTML.