我正在尝试填充变量parent_element_h1
和parent_element_h2
。任何人都可以帮助我使用Nokogiri来获取我需要的信息到这些变量中吗?
require 'rubygems'
require 'nokogiri'
value = Nokogiri::HTML.parse(<<-HTML_END)
"<html>
<body>
<p id='para-1'>A</p>
<div class='block' id='X1'>
<h1>Foo</h1>
<p id='para-2'>B</p>
</div>
<p id='para-3'>C</p>
<h2>Bar</h2>
<p id='para-4'>D</p>
<p id='para-5'>E</p>
<div class='block' id='X2'>
<p id='para-6'>F</p>
</div>
</body>
</html>"
HTML_END
parent = value.css('body').first
# start_here is given: A Nokogiri::XML::Element of the <div> with the id 'X2
start_here = parent.at('div.block#X2')
# this should be a Nokogiri::XML::Element of the nearest, previous h1.
# in this example it's the one with the value 'Foo'
parent_element_h1 =
# this should be a Nokogiri::XML::Element of the nearest, previous h2.
# in this example it's the one with the value 'Bar'
parent_element_h2 =
请注意:start_here
元素可以位于文档内的任何位置。 HTML数据只是一个例子。也就是说,标题<h1>
和<h2>
可以是start_here
的兄弟姐妹,也可以是start_here
兄弟的孩子。
以下递归方法是一个很好的起点,但它不适用于<h1>
,因为它是start_here
兄弟的孩子:
def search_element(_block,_style)
unless _block.nil?
if _block.name == _style
return _block
else
search_element(_block.previous,_style)
end
else
return false
end
end
parent_element_h1 = search_element(start_here,'h1')
parent_element_h2 = search_element(start_here,'h2')
接受答案后,我提出了my own solution。它就像一个魅力,我觉得它非常酷。
答案 0 :(得分:10)
我将采用的方法(如果我理解你的问题)是使用XPath或CSS来搜索你的“start_here”元素和你想要搜索的父元素。然后,从父节点开始递归地遍历树,当你点击“start_here”元素时停止,并保持与你的风格匹配的最后一个元素。
类似的东西:
parent = value.search("//body").first
div = value.search("//div[@id = 'X2']").first
find = FindPriorTo.new(div)
assert_equal('Foo', find.find_from(parent, 'h1').text)
assert_equal('Bar', find.find_from(parent, 'h2').text)
其中FindPriorTo
是一个处理递归的简单类:
class FindPriorTo
def initialize(stop_element)
@stop_element = stop_element
end
def find_from(parent, style)
@should_stop = nil
@last_style = nil
recursive_search(parent, style)
end
def recursive_search(parent, style)
parent.children.each do |ch|
recursive_search(ch, style)
return @last_style if @should_stop
@should_stop = (ch == @stop_element)
@last_style = ch if ch.name == style
end
@last_style
end
end
如果这种方法不够可扩展,那么您可以通过重写recursive_search
来不优化事物来不使用递归,并传递您正在寻找的两种样式并跟踪最后一种发现,所以你不必再多花时间穿过树。
我还会说在尝试解析文档时尝试使用Monkey修补Node来挂钩,但看起来所有这些都是用C语言编写的。也许你可能会更好地使用Nokogiri以外的其他东西使用本机Ruby SAX解析器(可能是REXML),或者如果速度是您真正关心的问题,请使用Xerces或类似工具在C / C ++中执行搜索部分。我不知道这些将如何处理解析HTML。
答案 1 :(得分:3)
我认为这几年来得太晚了,但觉得有必要发帖,因为所有其他解决方案都太复杂了。
这是XPath的一个声明:
start = doc.at('div.block#X2')
start.at_xpath('(preceding-sibling::h1 | preceding-sibling::*//h1)[last()]')
#=> <h2>Foo</h2>
start.at_xpath('(preceding-sibling::h2 | preceding-sibling::*//h2)[last()]')
#=> <h2>Bar</h2>
这适用于直接的兄弟姐妹或以前兄弟姐妹的孩子。无论哪一个匹配,last()
谓词都可确保您获得最接近的上一个匹配。
答案 2 :(得分:2)
也许会这样做。我不确定性能,是否有一些我没有想过的情况。
def find(root, start, tag)
ps, res = start, nil
until res or (ps == root)
ps = ps.previous || ps.parent
res = ps.css(tag).last
res ||= ps.name == tag ? ps : nil
end
res || "Not found!"
end
parent_element_h1 = find(parent, start_here, 'h1')
答案 3 :(得分:0)
这是我自己的解决方案(感谢我的同事帮助我解决这个问题!)使用递归方法解析所有元素,无论是兄弟姐妹还是其他兄弟姐妹的孩子。
require 'rubygems'
require 'nokogiri'
value = Nokogiri::HTML.parse(<<-HTML_END)
"<html>
<body>
<p id='para-1'>A</p>
<div class='block' id='X1'>
<h1>Foo</h1>
<p id='para-2'>B</p>
</div>
<p id='para-3'>C</p>
<h2>Bar</h2>
<p id='para-4'>D</p>
<p id='para-5'>E</p>
<div class='block' id='X2'>
<p id='para-6'>F</p>
</div>
</body>
</html>"
HTML_END
parent = value.css('body').first
# start_here is given: A Nokogiri::XML::Element of the <div> with the id 'X2
@start_here = parent.at('div.block#X2')
# Search for parent elements of kind "_style" starting from _start_element
def search_for_parent_element(_start_element, _style)
unless _start_element.nil?
# have we already found what we're looking for?
if _start_element.name == _style
return _start_element
end
# _start_element is a div.block and not the _start_element itself
if _start_element[:class] == "block" && _start_element[:id] != @start_here[:id]
# begin recursion with last child inside div.block
from_child = search_for_parent_element(_start_element.children.last, _style)
if(from_child)
return from_child
end
end
# begin recursion with previous element
from_child = search_for_parent_element(_start_element.previous, _style)
return from_child ? from_child : false
else
return false
end
end
# this should be a Nokogiri::XML::Element of the nearest, previous h1.
# in this example it's the one with the value 'Foo'
puts parent_element_h1 = search_for_parent_element(@start_here,"h1")
# this should be a Nokogiri::XML::Element of the nearest, previous h2.
# in this example it's the one with the value 'Bar'
puts parent_element_h2 = search_for_parent_element(@start_here,"h2")
您可以像运行ruby脚本一样将其复制/粘贴。
答案 4 :(得分:-1)
如果你不知道元素之间的关系,你可以这样搜索它们(文档中的任何地方):
# html code
text = "insert your html here"
# get doc object
doc = Nokogiri::HTML(text)
# get elements with the specified tag
elements = doc.search("//your_tag")
但是,如果您需要提交表格,则应使用机械化:
# create mech object
mech = WWW::Mechanize.new
# load site
mech.get("address")
# select a form, in this case, I select the first form. You can select the one you need
# from the array
form = mech.page.forms.first
# you fill the fields like this: form.name_of_the_field
form.element_name = value
form.other_element = other_value
答案 5 :(得分:-1)
您可以使用CSS选择器搜索Nokogiri HTML::Element
的后代。您可以使用.parent
方法遍历祖先。
parent_element_h1 = value.css("h1").first.parent
parent_element_h2 = value.css("h2").first.parent