Nokogiri 正在返回根元素。为什么?

Nokogiri is returning to root element. Why?

抱歉,这似乎是个题外话,请给我 2 分钟时间。我可能遗漏了一个小细节,但我对这段小代码感到疯狂:

parsed = Nokogiri::HTML(open(url))

fullmeta = parsed.xpath('//*[@id="profile_top"]')[0]
if fullmeta.inner_html.to_s.include? "image"
    meta = fullmeta.xpath('//span[4]')[0]
else
    meta = fullmeta.xpath('//span[3]')[0]
end

puts meta.inner_html                    # This seems fine
puts meta.xpath('//a[1]')[0].inner_html # !!!

标有!!!的行是罪魁祸首。某些东西正在使该行从 parsed 的根元素重新执行 XPath!我之前有那个变量声明了几个 XPath。什么。是。去。在。这里?我已经坐在这段代码上一个小时了! (DuckDuckGo 已经占据了一半的互联网)

如果您想要 XML,只需使用任何 FanFiction 故事页面即可。我在 rails 中为此写了一个 API,但这在这里并不是一个真正重要的事实。


万一有人用 FanFiction 尝试一下,这就是我得到的:

Rated: <a class="xcontrast_txt" href="https://www.fictionratings.com/" target="rating">Fiction  T</a> - English - Humor/Adventure - Chapters: 15   - Words: 55,643 - Reviews: <a href="/r/12135694/">22</a> - Favs: 5 - Follows: 8 - Updated: <span data-xutime="1501553985">17h</span> - Published: <span data-xutime="1473081239">9/5/2016</span> - id: 12135694 
FanFiction

最后一行应该是Fiction T

充分利用 XPath 的强大功能通常意味着您不必停下来进行迭代,只需使用一个表达式就可以直接抓取您想要的内容。这允许您外部化、存储在变量中或以其他方式组织您的表达式并更轻松地维护它们,即使 XML 发生变化。使用 XPath,您甚至可以在表达式中加入一些逻辑。

您要获取故事的评分吗?请注意,有一个 target=rating 属性,因此您可以关闭它,而不是计算 span 个元素。

doc.xpath('//*[@id="profile_top"]/span/a[@target="rating"]/text()')

#=> "Fiction M"

我推荐的另一件事是使用 HTTParty 或 Mechanize,如果您还没有的话。他们有不同的优势。 HTTParty 为您提供了一种简单的方法来创建具有抓取和解析功能的面向对象的客户端。 Mechanize 专注于抓取,但它内置了 Nokogiri,您可以访问底层的 Nokogiri 文档并开始在其上执行 XPath。

编辑: 从下面的评论中添加其他几个。

language = doc.xpath('//*[@id="profile_top"]/span[a[@target="rating"]]/text()').to_s.split(' - ')[1]
#=> "English"

请注意,括号 [] 可以读作 "which contains,",因此我们正在寻找范围 ,其中包含 一个 link 和一个评级目标。这样就不用计算spans了,比较脆。

genres = doc.xpath('//*[@id="profile_top"]/span[a[@target="rating"]]/text()').to_s.split(' - ')[2].split('/')
#=> ["Humor", "Adventure"]

id = doc.xpath('//*[@id="profile_top"]/span[a[@target="rating"]]/text()').to_s.split(' - ')[5].split(': ')
#=> "12596791"

published = DateTime.strptime(doc.xpath('//*[@id="profile_top"]//span/@data-xutime').first.value, '%s')
#=> 2017-08-01T20:03:19+00:00

等等。我建议将 XPath 放在类似散列的地方,这样您就可以参考更具描述性的 xpath_for[:rating] 而不是在整个代码中对它们进行硬编码。