如何用Nokogiri解析连续标签?

问题描述:

我有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>标签,我需要“的标签:值”

首先,你的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) –

看着对方的回答后,这里是做同样的事情的方式效率极低。

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

在一些<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"]]