我有一个非常简单的问题,我完全无法找到答案.我对Ruby和Nokogiri有非常基本的了解.
我的数据看起来像:
<release>
<artists>
<artist>
<name>Johnny Mnemonic</name>
</artist>
<artist>
<name>Constantine</name>
</artist>
<artists>
</release>
<release>
<artists>
<artist>
<name>Speed</name>
</artist>
<artist>
<name>The Matrix</name>
</artist>
<artists>
</release>
. . .and so on.
Run Code Online (Sandbox Code Playgroud)
对于每个版本,我只想要第一个标签中的数据.我尝试了以下代码,但它从艺术家那里获取:
page = Nokogiri::XML(open("37.xml"))
page.xpath("//artists[1]").each do |el|
File.open("#{LOCAL_DIR}/37.txt", 'a'){|f| f.write(el)}
Run Code Online (Sandbox Code Playgroud)
任何有关正确方向的帮助或指示都将受到极大的赞赏.
Nokogiri支持两种主要类型的搜索,search以及at.search返回一个NodeSet,您应该将其视为一个数组.at返回一个节点.要么可以采用CSS或XPath表达式.我更喜欢CSS,因为它们更具可读性,但有时你不能轻易地获得你想要的地方,所以试试另一个.
对于您的问题,重要的是指定要从中提取文本的节点,使用text.如果结果太宽,除了标签内的文字外,您还会从标签之间获取文字.为了避免深入到您想要阅读的最直接的节点:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<release>
<artists>
<artist>
<name>Johnny Mnemonic</name>
</artist>
<artist>
<name>Constantine</name>
</artist>
<artists>
<release>
EOT
Run Code Online (Sandbox Code Playgroud)
因为这些name特定地查找节点,所以在没有垃圾的情况下很容易获得所需的文本:
doc.at('name').text # => "Johnny Mnemonic"
doc.at('artist name').text # => "Johnny Mnemonic"
doc.at('artists artist name').text # => "Johnny Mnemonic"
Run Code Online (Sandbox Code Playgroud)
这些是更宽松的搜索,因此返回更多的垃圾:
doc.at('artist').text # => "\n Johnny Mnemonic\n "
doc.at('artists').text # => "\n \n Johnny Mnemonic\n \n \n Constantine\n \n \n\n"
Run Code Online (Sandbox Code Playgroud)
使用search返回多个节点:
doc.search('name').map(&:text)
[
[0] "Johnny Mnemonic",
[1] "Constantine"
]
doc.search('artist').map(&:text)
[
[0] "\n Johnny Mnemonic\n ",
[1] "\n Constantine\n "
]
Run Code Online (Sandbox Code Playgroud)
之间唯一的区别search和at是at像search(...).first.
请参阅" 如何避免在刮取时加入节点中的所有文本 ".
为方便起见,Nokogiri有一些额外的别名:at_css和css,at_xpath和xpath.
以下是使用CSS和XPath访问器获取名称的替代方法,从Pry剪切:
[5] (pry) main: 0> # using CSS with Ruby
[6] (pry) main: 0> artists = doc.search('release').map{ |release| release.at('artist').text.strip }
[
[0] "Johnny Mnemonic",
[1] "Speed"
]
[7] (pry) main: 0> # using CSS with less Ruby
[8] (pry) main: 0> artists = doc.search('release artists artist:nth-child(1) name').map{ |n| n.text }
[
[0] "Johnny Mnemonic",
[1] "Speed"
]
[9] (pry) main: 0>
[10] (pry) main: 0> # using XPath
[11] (pry) main: 0> artists = doc.search('release/artists/artist[1]/name').map{ |t| t.content }
[
[0] "Johnny Mnemonic",
[1] "Speed"
]
[12] (pry) main: 0> # using more XPath
[13] (pry) main: 0> artists = doc.search('release/artists/artist[1]/name/text()').map{ |t| t.content }
[
[0] "Johnny Mnemonic",
[1] "Speed"
]
Run Code Online (Sandbox Code Playgroud)