2011-12-13 64 views
4

我有HTML代碼:如何用Nokogiri解析連續標籤?

<div id="first"> 
<dt>Label1</dt> 
<dd>Value1</dd> 

<dt>Label2</dt> 
<dd>Value2</dd> 

... 
</div> 

我的代碼不能正常工作。

doc.css("first").each do |item| 
    label = item.css("dt") 
    value = item.css("dd") 
end 

顯示所有<dt>標籤的第一次,然後<dd>標籤,我需要「的標籤:值」

回答

5

首先,你的HTML應該有一個<dl><dt><dd>元素:

<div id="first"> 
    <dl> 
     <dt>Label1</dt> 
     <dd>Value1</dd> 
     <dt>Label2</dt> 
     <dd>Value2</dd> 
     ... 
    </dl> 
</div> 

但不會改變你如何解析它。你想找到<dt> s並迭代它們,然後在每個<dt>你可以使用next_element得到<dd>;像這樣:

doc = Nokogiri::HTML('<div id="first"><dl>...') 
doc.css('#first').search('dt').each do |node| 
    puts "#{node.text}: #{node.next_element.text}" 
end 

這應該工作,只要結構符合您的示例。

+0

這是作品,謝謝你們! – jgiunta

+0

而不是'doc.css('#first')。search('dt')。each'爲什麼不只是'doc.css('#first dt')。each'?還要注意,這個答案在假設在每個'

'之後總是有一個且只有一個'
'(在一般的HTML中可能不是這種情況)的情況下工作。 – Phrogz

+1

@Progrog:'.css.search'沒有什麼好的理由,但也許它更接近OP已經擁有的。而且我確實包括了「只要結構與您的示例相匹配就應該有效」的警告。我同意你的方法在一般情況下會更好。 (這只是我最後一個評論的拼寫糾正,因爲我是dun haz gud speling) –

0

看着對方的回答後,這裏是做同樣的事情的方式效率極低。

require 'nokogiri' 
a = Nokogiri::HTML('<div id="first"><dt>Label1</dt><dd>Value1</dd><dt>Label2</dt><dd>Value2</dd></div>') 

dt = [] 
dd = [] 

a.css("#first").each do |item| 
    item.css("dt").each {|t| dt << t.text} 
    item.css("dd").each {|t| dd << t.text} 
end 

dt.each_index do |i| 
    puts dt[i] + ': ' + dd[i] 
end 

在css引用ID你需要把#符號之前。對於一個班級來說是這樣的。符號。

+0

哦。現在有道理。 –

+0

請注意,由於'「#first」'只能匹配一個元素,因此您所擁有的與'item = a.at_css(「#first」)'等效(但更糟糕)。在外面使用'each'完全是多餘的。 – Phrogz

+0

此外,請注意,這個答案假定在'

'和'
'之間總是有一對一的1-1配對。儘管原始問題標記的確如此,但在現實世界的標記中可能並非總是如此。最後,迭代兩個配對數組,你可以考慮使用'dt.zip(dd).each {| dt,dd | ...}而不是'each_with_index'。 – Phrogz

4

在一些<dt>可能有多個<dd>的假設下,要找到所有<dt>然後(每個)尋找下一個<dt>之前以下<dd>。這在純Ruby中很容易實現,但是在XPath中做起來更有趣。 ;)

鑑於此設置:

require 'nokogiri' 
html = '<dl id="first"> 
    <dt>Label1</dt><dd>Value1</dd> 
    <dt>Label2</dt><dd>Value2</dd> 
    <dt>Label3</dt><dd>Value3a</dd><dd>Value3b</dd> 
    <dt>Label4</dt><dd>Value4</dd> 
</dl>'  
doc = Nokogiri.HTML(html) 

使用沒有的XPath

doc.css('dt').each do |dt| 
    dds = [] 
    n = dt.next_element 
    begin 
    dds << n 
    n = n.next_element 
    end while n && n.name=='dd' 
    p [dt.text,dds.map(&:text)] 
end 
#=> ["Label1", ["Value1"]] 
#=> ["Label2", ["Value2"]] 
#=> ["Label3", ["Value3a", "Value3b"]] 
#=> ["Label4", ["Value4"]] 

使用小的XPath

doc.css('dt').each do |dt| 
    dds = dt.xpath('following-sibling::*').chunk{ |n| n.name }.first.last 
    p [dt.text,dds.map(&:text)] 
end 
#=> ["Label1", ["Value1"]] 
#=> ["Label2", ["Value2"]] 
#=> ["Label3", ["Value3a", "Value3b"]] 
#=> ["Label4", ["Value4"]] 

使用Lotsa的XPath

doc.css('dt').each do |dt| 
    ct = dt.xpath('count(following-sibling::dt)') 
    dds = dt.xpath("following-sibling::dd[count(following-sibling::dt)=#{ct}]") 
    p [dt.text,dds.map(&:text)] 
end 
#=> ["Label1", ["Value1"]] 
#=> ["Label2", ["Value2"]] 
#=> ["Label3", ["Value3a", "Value3b"]] 
#=> ["Label4", ["Value4"]]