我使用XPath选择器选择页面上的每个项目(大约24个),然后我在每个项目上使用XPath选择器返回每个项目的值。
即使我在子节点上运行XPath选择器,它似乎正在搜索所有子节点,我只想在每个子节点上单独完成。
以下代码搜索doc
上的每个项目,然后迭代每个html_listing
。然后将其传递给get_field_data_from
:
def get_listing(doc,field_data = {})
doc.xpath(get_listing_tag[:path]).each do |html_listing|
fd = get_field_data_from(html_listing,field_data)
if !field_data && fd.detect {|_,data| !data }
set_uri doc.xpath(get_sub_page_tag[:path])
get
fd = get_listing(Nokogiri::HTML(body),fd)
end
yield fd
end
end
所以它遍历所有Fields
我正在寻找哪个用于检索包含字符串的XPath选择器
selector = send("get_%s_tag" % field)
如果选择器存在且尚未找到数据,它将使用HTML item
上的XPath选择器,使用
res[field] = item.xpath(selector[:path]).inner_text
然后返回在下一次迭代中使用的结果哈希。
def get_field_data_from(item,data)
Fields.inject(data) do |res,field|
selector = send("get_%s_tag" % field)
unless !selector || res[field]
begin
res[field] = item.xpath(selector[:path]).inner_text
rescue Exception => e
puts "Error for field: %s" % field
raise e
end
end
res
end
end
似乎在做什么
res[field] = item.xpath(selector[:path]).inner_text
它似乎搜索了所有项目,而不仅仅是搜索项目列表。我知道这样做是因为:
这样做的:
puts item.xpath(selector[:path]).inner_text
返回多个结果
我实际上并没有遍历所有的html_listings。如果它在yield fd
中生成字段数据get_listing
,我会执行break
,因此它只执行一次。
我似乎无法弄清楚发生了什么。别人看到了吗?
答案 0 :(得分:1)
您需要在元素上锚定XPath查询:
node.xpath("//example")
进行全局搜索node.xpath(".//example")
执行从当前节点开始的本地搜索注意前导点.
,它将查询锚定在当前节点上。否则,即使您从当前节点调用查询,也会针对根节点运行查询。
如果您按标签名称搜索,请考虑使用CSS选择器。他们比XPath有更少的陷阱。 CSS总是从当前节点进行搜索。
答案 1 :(得分:1)
还有另一个同样严重的问题。
item.xpath(selector[:path]).inner_text
xpath
返回一个NodeSet。 inner_text
将连接NodeSet中所有节点的结果,从而产生一个通常不会成为你想要的字符串。
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<p>foo</p>
<p>bar</p>
</body>
</html>
EOT
doc.search('p').class # => Nokogiri::XML::NodeSet
doc.search('p').inner_text # => "foobar"
相反,您需要使用map
来遍历节点列表,然后获取文本:
doc.search('p').map(&:inner_text) # => ["foo", "bar"]
或者,为简单起见:
doc.search('p').map(&:text) # => ["foo", "bar"]
参见&#34; How to avoid joining all text from Nodes when scraping&#34;还