I tried to make iteration of some <li>
elements
Iteration won't work as expected:
require 'nokogiri'
doc = Nokogiri::HTML(<<-END_OF_HTML)
<ul >
<li>
<input type="radio" name="group_4" value="709" id="comb_709_group_4" checked="checked">
<label for="comb_709_group_4" >
<span >1-10 kg</span>
<span >14.85 €</span>
</label>
<span >1-10 kg</span>
</li>
<li id="comb_710_group_4_li">
<input type=" radio" name="group_4" value="710" id="comb_710_group_4">
<label for="comb_710_group_4">
<span >10-20 kg</span>
<span >17.82 €</span>
</label>
<span >10-20 kg</span>
</li>
<li id="comb_711_group_4_li">
<input type=" radio" name="group_4" value="711" id="comb_711_group_4">
<label for="comb_711_group_4">
<span >20-40 kg</span>
<span >19.80 €</span>
</label>
<span >20-40 kg</span></li>
</ul>
END_OF_HTML
lis = doc.xpath("//li")
lis.each do |li|
p li.xpath("//span[@class = 'price_comb']/text()").to_s
end
returns this:
"14.85 €17.82 €19.80 €"
"14.85 €17.82 €19.80 €"
"14.85 €17.82 €19.80 €"
But I'm expected to see this:
"14.85 €"
"17.82 €"
"19.80 €"
Why xpath
works so strange and how can I fix that?
CodePudding user response:
You are missing a dot .
at the beginning of your XPath expression.
Instead of
"//span[@class = 'price_comb']/text()"
It should be
".//span[@class = 'price_comb']/text()"
So the entire code piece will be:
lis.each do |li|
p li.xpath(".//span[@class = 'price_comb']/text()").to_s
end
This XPath expression //span[@class = 'price_comb']/text()
is searching from the top of the document, not inside a specific node.
To make it search inside a node you should start the expression with a dot .
: .//span[@class = 'price_comb']/text()
UPDATE
As mentioned by engineersmnky and may be useful:
- The dot
.
is a relative path, meaning it will only search inside the node. - The double slash "//" means anywhere inside this node;
- where as a single slash "/" would be just direct descendants.
- Xpath Cheatsheet might help give you the basics