XPath 背后的想法之一是它允许我们像导航磁盘目录一样导航 DOM:
require 'hpricot'
xml = <<EOT
<rss>
<channel>
<item>
<title>Book1</title>
<pubDate>march 1 2010</pubDate>
<author>Bob</author>
</item>
<item>
<title>book2</title>
<pubDate>october 4 2009</pubDate>
<author>Bill</author>
</item>
<item>
<title>book3</title>
<pubDate>June 5 2010</pubDate>
<author>Steve</author>
</item>
<item>
<title>Book4</title>
<pubDate>march 1 2010</pubDate>
<author>Bob</author>
</item>
</channel>
</rss>
EOT
doc = Hpricot(xml)
titles = (doc / '//author[text()="Bob"]/../title' )
titles # => #<Hpricot::Elements[{elem <title> "Book1" </title>}, {elem <title> "Book4" </title>}]>
这意味着:“找到 Bob 的所有书籍,然后查找一级并找到标题标签”。
我添加了一本“鲍勃”的额外书来测试所有事件。
要获得包含 Bob 的书的项目,只需向上移动一个级别:
items = (doc / '//author[text()="Bob"]/..' )
puts items # => nil
# >> <item>
# >> <title>Book1</title>
# >> <pubdate>march 1 2010</pubdate>
# >> <author>Bob</author>
# >> </item>
# >> <item>
# >> <title>Book4</title>
# >> <pubdate>march 1 2010</pubdate>
# >> <author>Bob</author>
# >> </item>
我也弄清楚了(doc % :rss % :channel / :item)
在做什么。这相当于嵌套搜索,减去换行括号,这些在 Hpricot-ese 中应该都是相同的:
(doc % :rss % :channel / :item).size # => 4
(((doc % :rss) % :channel) / :item).size # => 4
(doc / '//rss/channel/item').size # => 4
(doc / 'rss channel item').size # => 4
因为'//rss/channel/item'
这是您通常看到的 XPath 访问器,并且'rss channel item'
是 CSS 访问器,所以我建议使用这些格式来维护和清晰。