我需要找到从以下XML数据中收集作家和艺术家信息的最佳方法。 comic
节点多次出现,包括单个漫画书的数据。
我不能根据他们的工作职位,作家,艺术家等来抓住合适的人。每个漫画书有时会有多位作家和艺术家。我的计划是将每个添加/附加到List。
所以,对于这本单一的漫画书,我需要让所有的作家和#39;和艺术家'显示名称,但作业功能(例如作家)是人名的兄弟。
这是我所拥有的,但不起作用:
writer = []
penciler = []
doc.xpath('//comic').each do |main_element|
main_element.xpath("mainsection/credits/credit/role[@id='dfWriter']").each do |n|
writer << n.xpath('person/displayname').text
end
main_element.xpath("mainsection/credits/credit/role[@id='dfPenciler']").each do |n|
penciler << n.xpath('person/displayname').text
end
end
p "Writer(s): ",writer
p "Penciler(s): ",penciler
这是XML文件/数据:
<comic>
<id>3398</id>
<index>195</index>
<mainsection>
<title>Mind Games</title>
<myrating>0</myrating>
<myrating>
<displayname>0</displayname>
<sortname>0</sortname>
</myrating>
<pagecount>32</pagecount>
<credits>
<credit>
<role id="dfWriter">Writer</role>
<roleid>dfWriter</roleid>
<person>
<displayname>Will Pfeifer</displayname>
<sortname>Pfeifer, Will</sortname>
<lastname>Pfeifer</lastname>
<firstname>Will</firstname>
</person>
</credit>
<credit>
<role id="dfWriter">Writer</role>
<roleid>dfWriter</roleid>
<person>
<displayname>John Byrne</displayname>
<sortname>Byrne, John</sortname>
<lastname>Byrne</lastname>
<firstname>John</firstname>
</person>
</credit>
<credit>
<role id="dfPenciler">Penciller</role>
<roleid>dfPenciler</roleid>
<person>
<displayname>John Byrne</displayname>
<sortname>Byrne, John</sortname>
<lastname>Byrne</lastname>
<firstname>John</firstname>
</person>
</credit>
</credits>
</mainsection>
</comic>
我的代码没有给我预期的结果。我发现&#34; Getting the siblings of a node with Nokogiri&#34;但我需要迭代并抓住每个兄弟姐妹。
我可以按<roleid>dfWriter</roleid>
或<role id="dfWriter">Writer</role>
进行搜索,因为它们是相同的。
我的预期输出是:
Writer(s): Will Pfeifer, John Byrne
Penciler(s): John Byrne
答案 0 :(得分:1)
为此目的,您可以使用XPath following-sibling
轴,假设目标元素始终位于 role
之后:
doc.xpath('//comic').each do |main_element|
main_element.xpath("mainsection/credits/credit/role[@id='dfWriter']").each do |n|
writer << n.xpath('following-sibling::person/displayname').text
end
main_element.xpath("mainsection/credits/credit/role[@id='dfPenciler']").each do |n|
penciler << n.xpath('following-sibling::person/displayname').text
end
end
或者您可以首先遍历credit
而不是role
:
doc.xpath('//comic').each do |main_element|
main_element.xpath("mainsection/credits/credit[role/@id='dfWriter']").each do |n|
writer << n.xpath('person/displayname').text
end
main_element.xpath("mainsection/credits/credit[role/@id='dfPenciler']").each do |n|
penciler << n.xpath('person/displayname').text
end
end
答案 1 :(得分:0)
以下是我如何做到这一点:
require 'nokogiri'
XML = <<EOT
<comic>
<mainsection>
<credits>
<credit>
<role id="dfWriter">Writer</role>
<person>
<displayname>Will Pfeifer</displayname>
</person>
</credit>
<credit>
<role id="dfWriter">Writer</role>
<person>
<displayname>John Byrne</displayname>
</person>
</credit>
<credit>
<role id="dfPenciler">Penciller</role>
<person>
<displayname>John Byrne</displayname>
</person>
</credit>
</credits>
</mainsection>
</comic>
EOT
doc = Nokogiri::XML(XML)
writers = doc.search("credits role[id='dfWriter']").map { |w| w.parent.at('displayname').text }
pencilers = doc.search("credits role[id='dfPenciler']").map { |n| n.parent.at('displayname').text }
puts "Writer(s): %s" % writers.join(', ')
puts "Penciler(s): %s" % pencilers.join(', ')
# >> Writer(s): Will Pfeifer, John Byrne
# >> Penciler(s): John Byrne
运行时输出:
# >> Writer(s): Will Pfeifer, John Byrne
# >> Penciler(s): John Byrne
此:
writers = doc.search("credits role[id='dfWriter']").map { |w| w.parent.at('displayname').text }
pencilers = doc.search("credits role[id='dfPenciler']").map { |n| n.parent.at('displayname').text }
可能会干嘛:
writers, pencilers = %w(dfWriter dfPenciler).map { |s|
doc.search("credits role[id='#{s}']").map { |w| w.parent.at('displayname').text }
}
我使用CSS来提高可读性,而at
则返回一个Node,当我想要文本而不是xpath
时,返回一个NodeSet。
在NodeSet上使用text
与节点之间的区别非常重要。考虑一下:
require 'nokogiri'
xml = <<EOT
<root>
<displayname>Will Pfeifer</displayname>
<displayname>John Byrne</displayname>
<displayname>John Byrne</displayname>
</root>
EOT
doc = Nokogiri::XML(xml)
doc.search('displayname').class # => Nokogiri::XML::NodeSet
doc.search('displayname').text # => "Will PfeiferJohn ByrneJohn Byrne"
doc.at('displayname').class # => Nokogiri::XML::Element
doc.at('displayname').text # => "Will Pfeifer"
如果您希望NodeSet的所有文本都以易于使用的形式提取,则从每个节点中提取它:
doc.search('displayname').map(&:text) # => ["Will Pfeifer", "John Byrne", "John Byrne"]