Home > Software engineering >  Iterations of elements in Nokogiri xpath
Iterations of elements in Nokogiri xpath

Time:12-31

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
  • Related