Как мне разобрать структуру тегов LI / DL / DD, используя Ruby и Nokogiri? - PullRequest
1 голос
/ 18 мая 2011

Я пытаюсь разобрать html, который содержит как упорядоченный список, так и теги DL / DD.Цель состоит в том, чтобы создать структуру XML, которая разбивает содержимое каждого тега, добавляя некоторый атрибут.В конце эффект выравнивает структуру (желаемый результат будет показан в конце вопроса).

Вот пример HTML-файла, хранящегося в файле (который содержится в test.html в моем коде):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">  
<head>  
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
<title>Test Structure</title>  
</head>  
<body>  
<ol><li>Item 1 - Level 1  
<dl><dd>Item 1.1 - Level 2  
</dd><dd>Item 1.2 - Level 2  
</dd></dl>  
</li><li>Item 2 - Level 1  
<dl><dd>Item 2.1 - Level 2  
<dl><dd>Item 2.1.1 - Level 3  
</dd><dd>Item 2.1.2 - Level 3  
<dl><dd>Item 2.1.2.1 - Level 4  
</dd><dd>Item 2.1.2.2 - Level 4  
</dd></dl>  
</dd></dl>  
</dd><dd>Item 2.2 - Level 2  
<dl><dd>Item 2.2.1 - Level 3  
</dd><dd>Item 2.2.2 - Level 3  
<dl><dd>Item 2.2.2.1 - Level 4  
</dd><dd>Item 2.2.2.2 - Level 4  
</dd></dl>  
</dd><dd>Item 2.2.3 - Level 3  
<dl><dd>Item 2.2.3.1 - Level 4  
</dd><dd>Item 2.2.3.2 - Level 4  
</dd></dl>  
</dd><dd>Item 2.2.4 - Level 3  
</dd></dl>  
</dd></dl>  
</li><li>Item 3 - Level 1  
<dl><dd>Item 3.1 - Level 2  
</dd><dd>Item 3.2 - Level 2  
</dd></dl>  
</li></ol>  
</body>  
</html>

Вывод из HTML (здесь показано, что вы не видите отступ, который вы видите в браузере):

<ol><li>Item 1 - Level 1
<dl><dd>Item 1.1 - Level 2
</dd><dd>Item 1.2 - Level 2
</dd></dl>
</li><li>Item 2 - Level 1
<dl><dd>Item 2.1 - Level 2
<dl><dd>Item 2.1.1 - Level 3
</dd><dd>Item 2.1.2 - Level 3
<dl><dd>Item 2.1.2.1 - Level 4
</dd><dd>Item 2.1.2.2 - Level 4
</dd></dl>
</dd></dl>
</dd><dd>Item 2.2 - Level 2
<dl><dd>Item 2.2.1 - Level 3
</dd><dd>Item 2.2.2 - Level 3
<dl><dd>Item 2.2.2.1 - Level 4
</dd><dd>Item 2.2.2.2 - Level 4
</dd></dl>
</dd><dd>Item 2.2.3 - Level 3
<dl><dd>Item 2.2.3.1 - Level 4
</dd><dd>Item 2.2.3.2 - Level 4
</dd></dl>
</dd><dd>Item 2.2.4 - Level 3
</dd></dl>
</dd></dl>
</li><li>Item 3 - Level 1
<dl><dd>Item 3.1 - Level 2
</dd><dd>Item 3.2 - Level 2
</dd></dl>
</li></ol>

Желаемый вывод:

<job>  
<req level='1'>Item 1 - Level 1</req>  
<req level='1.1'>Item 1.1 - Level 2</req>  
<req level='1.2'>Item 1.2 - Level 2</req>  
<req level='2'>Item 2 - Level 1</req>  
<req level='2.1'>Item 2.1 - Level 2</req>  
<req level='2.1.1'>Item 2.1.1 - Level 3</req>  
<req level='2.1.2'>Item 2.1.2 - Level 3</req>  
<req level='2.1.2.1'>Item 2.1.2.1 - Level 4</req>  
<req level='2.1.2.2'>Item 2.1.2.2 - Level 4</req>  
<req level='2.2'>Item 2.2 - Level 2</req>  
<req level='2.2.1'>Item 2.2.1 - Level 3</req>  
<req level='2.2.2'>Item 2.2.2 - Level 3</req>  
<req level='2.2.2.1'>Item 2.2.2.1 - Level 4</req>  
<req level='2.2.2.2'>Item 2.2.2.2 - Level 4</req>  
<req level='2.2.3'>Item 2.2.3 - Level 3</req>  
<req level='2.2.3.1'>Item 2.2.3.1 - Level 4</req>  
<req level='2.2.3.2'>Item 2.2.3.2 - Level 4</req>  
<req level='2.2.4'>Item 2.2.4 - Level 3</req>  
<req level='3'>Item 3 - Level 1</req>  
<req level='3.1'>Item 3.1 - Level 2</req>  
<req level='3.2'>Item 3.2 - Level 2</req>  
</job>

Обратите внимание, чтомы хотим вывести иерархию из обхода структуры, а не из фактического содержимого каждого атрибута LI и DD ... содержимое моего примера перечисляет иерархию (1, 1.1, 1.2 ...), но в реальных данных, которые мыне увидит этого.Атрибут «level» должен отражать обход структуры.

Я новичок как в Ruby, так и в Nokogiri, но вот моя попытка чтения HTML (не надо создавать XML).Я застрял, отделяя узлы LI и содержимое.Я пытался использовать .each, children.each и т. Д .:

require 'rubygems'  
require 'open-uri'  
require 'nokogiri'  

url = "test.html"  
doc = Nokogiri::HTML(open(url))  
line = "1"  
doc.css("ol[1]").children.each do |n|  
    puts line + n.content.to_s  
    line.succ!  
    n.children do |c|  
        puts line + c.content.to_s  
        line.succ!  
    end  
end  

1 Ответ

1 голос
/ 18 мая 2011

Вы можете использовать метод node_name, чтобы определить, что такое текст и что такое дочерний элемент. Вот пример функции, которая выплевывает имя тегов html под ol:

def traverse(node, indent = 0)
  node.children.each do |child|
    next if child.node_name == "text"
    puts "  "*indent + child.node_name
    traverse(child, indent+1)
  end
end

traverse doc.css("ol[1]")

(текстовые узлы, которые я пропускаю выше, являются текстовым содержимым тегов)

...