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



对不起,这似乎是一个题外话,但给我 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,只需使用任何同人小说故事页面。我正在为它编写一个 Rails 的 API,但这在这里并不是一个重要的事实。


以防万一有人尝试使用同人小说,这就是我得到的:

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"

请注意,括号[]可以读作"包含",因此我们正在寻找包含具有评级目标的链接跨度。这样你就不需要计算跨度,这更脆。

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

等等。我建议将 XPaths 放在类似哈希的东西中,这样您就可以引用更具描述性的xpath_for[:rating],而不是在整个代码中对它们进行硬编码。

最新更新