Теги parse 'ul' и 'ol'
Я должен обрабатывать глубокую вложенность тегов ul
, ol
и li
. Мне нужно дать ту же точку зрения, что и в браузере. Я хочу получить следующий пример в pdf файле:
text = "
<body>
<ol>
<li>One</li>
<li>Two
<ol>
<li>Inner One</li>
<li>inner Two
<ul>
<li>hey
<ol>
<li>hiiiiiiiii</li>
<li>why</li>
<li>hiiiiiiiii</li>
</ol>
</li>
<li>aniket </li>
</li>
</ul>
<li>sup </li>
<li>there </li>
</ol>
<li>hey </li>
<li>Three</li>
</li>
</ol>
<ol>
<li>Introduction</li>
<ol>
<li>Introduction</li>
</ol>
<li>Description</li>
<li>Observation</li>
<li>Results</li>
<li>Summary</li>
</ol>
<ul>
<li>Introduction</li>
<li>Description
<ul>
<li>Observation
<ul>
<li>Results
<ul>
<li>Summary</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Overview</li>
</ul>
</body>"
Я должен использовать креветку для своей задачи. Но креветка не поддерживает HTML-теги. Итак, я придумал решение, использующее nokogiri
:. Я разбираюсь, а затем удаляю теги с помощью gsub. Нижеследующее решение, которое я написал для части вышеуказанного контента, но проблема в том, что ul и ol могут варьироваться.
RULES = {
ol: {
1 => ->(index) { "#{index + 1}. " },
2 => ->(index) { "#{}" },
3 => ->(index) { "#{}" },
4 => ->(index) { "#{}" }
},
ul: {
1 => ->(_) { "\u2022 " },
2 => ->(_) { "" },
3 => ->(_) { "" },
4 => ->(_) { "" },
}
}
def ol_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
prefix = RULES[:ol][deepness].call(i)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def ul_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
prefix = RULES[:ul][deepness].call(i)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def descend(item, deepness)
item.search('> ol').each do |ol|
ol_rule(ol, deepness: deepness)
end
item.search('> ul').each do |ul|
ul_rule(ul, deepness: deepness)
end
end
doc = Nokogiri::HTML.fragment(text)
doc.search('ol').each do |group|
ol_rule(group, deepness: 1)
end
doc.search('ul').each do |group|
ul_rule(group, deepness: 1)
end
puts doc.inner_text
1. One
2. Two
1. Inner One
2. inner Two
• hey
1. hiiiiiiiii
2. why
3. hiiiiiiiii
• aniket
3. sup
4. there
3. hey
4. Three
1. Introduction
1. Introduction
2. Description
3. Observation
4. Results
5. Summary
• Introduction
• Description
• Observation
• Results
• Summary
• Overview
проблема
1) Я хочу достичь того, как обрабатывать пространство при работе с тегами ul и ol
2) Как обращаться с глубоким вложением, когда li входит внутрь ul или li, входит внутрь
Ответы
Ответ 1
Во-первых, для обработки пространства я использовал взломать лямбда-вызов. Кроме того, я использую функцию add_previous_sibling, заданную nokogiri, чтобы добавить что-то в начало. Наконец, креветка не обрабатывает пространство, когда мы имеем дело с тегами ul & ol, поэтому для этого я использовал этот gsub gsub (/^ ([^\S\r\n] +)/m) {| m | "\ xC2\xA0" * m.size}. Вы можете прочитать больше из этой ссылки
Примечание. Nokogiri не обрабатывает недопустимый HTML, поэтому всегда предоставляйте действительный HTML-код.
RULES = {
ol: {
1 => ->(index) { "#{index + 1}. " },
2 => ->(index) { "#{}" },
3 => ->(index) { "#{}" },
4 => ->(index) { "#{}" }
},
ul: {
1 => ->(_) { "\u2022 " },
2 => ->(_) { "" },
3 => ->(_) { "" },
4 => ->(_) { "" },
},
space: {
1 => ->(index) { " " },
2 => ->(index) { " " },
3 => ->(index) { " " },
4 => ->(index) { " " },
}
}
def ol_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
prefix = RULES[:ol][deepness].call(i)
space = RULES[:space][deepness].call(i)
item.add_previous_sibling(space)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def ul_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
space = RULES[:space][deepness].call(i)
prefix = RULES[:ul][deepness].call(i)
item.add_previous_sibling(space)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def descend(item, deepness)
item.search('> ol').each do |ol|
ol_rule(ol, deepness: deepness)
end
item.search('> ul').each do |ul|
ul_rule(ul, deepness: deepness)
end
end
doc = Nokogiri::HTML.parse(text)
doc.search('ol').each do |group|
ol_rule(group, deepness: 1)
end
doc.search('ul').each do |group|
ul_rule(group, deepness: 1)
end
Prawn::Document.generate("hello.pdf") do
#puts doc.inner_text
text doc.at('body').children.to_html.gsub(/^([^\S\r\n]+)/m) { |m| "\xC2\xA0" * m.size }.gsub("<ul>","").gsub("<\/ul>","").gsub("<ol>","").gsub("<\/ol>","").gsub("<li>", "").gsub("</li>","").gsub("\\n","").gsub(/[\n]+/, "\n")
end
Ответ 2
Я придумал решение, которое обрабатывает несколько идентификаторов с настраиваемыми правилами нумерации на уровне:
require 'nokogiri'
ROMANS = %w[i ii iii iv v vi vii viii ix]
RULES = {
ol: {
1 => ->(index) { "#{index + 1}. " },
2 => ->(index) { "#{('a'..'z').to_a[index]}. " },
3 => ->(index) { "#{ROMANS.to_a[index]}. " },
4 => ->(index) { "#{ROMANS.to_a[index].upcase}. " }
},
ul: {
1 => ->(_) { "\u2022 " },
2 => ->(_) { "\u25E6 " },
3 => ->(_) { "* " },
4 => ->(_) { "- " },
}
}
def ol_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
prefix = RULES[:ol][deepness].call(i)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def ul_rule(group, deepness: 1)
group.search('> li').each_with_index do |item, i|
prefix = RULES[:ul][deepness].call(i)
item.prepend_child(prefix)
descend(item, deepness + 1)
end
end
def descend(item, deepness)
item.search('> ol').each do |ol|
ol_rule(ol, deepness: deepness)
end
item.search('> ul').each do |ul|
ul_rule(ul, deepness: deepness)
end
end
doc = Nokogiri::HTML.fragment(text)
doc.search('ol:root').each do |group|
binding.pry
ol_rule(group, deepness: 1)
end
doc.search('ul:root').each do |group|
ul_rule(group, deepness: 1)
end
Затем вы можете удалить теги или использовать doc.inner_text в зависимости от вашей среды.
Два оговорки:
- Селектор ввода должен быть тщательно выбран. Я использовал ваш фрагмент дословно без корневого элемента, поэтому мне пришлось использовать ul: root/ol: root. Возможно, "body> ol" тоже работает для вашей ситуации. Возможно, выбирая каждый ol/ul, а не ходячий, и только найдите тех, у кого нет родителя списка.
- Используя ваш пример дословно, Nokogiri не обрабатывает последние 2 элемента списка первой группы очень хорошо ("эй", "три") При разборе с nokogiri, таким образом, элементы уже "оставили" свое дерево ol и попали в корень дерево.
Токовый выход:
1. One
2. Two
a. Inner One
b. inner Two
◦ hey
◦ hey
3. hey
4. hey
hey
Three
1. Introduction
a. Introduction
2. Description
3. Observation
4. Results
5. Summary
• Introduction
• Description
◦ Observation
* Results
- Summary
• Overview
Ответ 3
Всякий раз, когда вы находитесь в ol
, li
или ul
, вы должны рекурсивно проверять ol
, li
и ul
. Если их нет, верните (что было обнаружено как подструктура), если есть, вызовите ту же функцию на новом узле и добавьте ее возвращаемое значение в текущую структуру.
Вы выполняете другое действие на каждом узле независимо от того, где оно зависит от его типа, а затем функция автоматически переупаковывает все.