كيفية التنقل في DOM باستخدام Nokogiri
-
19-08-2019 - |
سؤال
وأنا أحاول ملء 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')
وبعد قبول جوابا، خطرت لي بلدي حل . وهي تعمل مثل السحر واعتقد انها باردة جدا.
المحلول
وجئت عبر هذا بضع سنوات في وقت متأخر جدا أفترض، ولكن مضطرا للنشر لأن كل الحلول الأخرى هي وسيلة معقدة للغاية.
وانها قائمة واحدة مع كسباث:
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()
يضمن لك الحصول على أقرب المباراة السابقة.
نصائح أخرى
والنهج وأود أن أنتهز (إذا أنا فهم مشكلتك) هو استخدام كسباث أو 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
عدم استخدام العودية، وأيضا تمرير في كل من الأنماط التي تبحث عن وتتبع الماضي وجدت، لذلك كنت ليس لديك لاجتياز شجرة في الوقت الاضافي.
وأقول أيضا محاولة قرد الترقيع عقدة لربط عندما يتم الحصول على تحليل الوثيقة، ولكن يبدو أن كل ما هو مكتوب في C. ربما كنت قد يكون من الأفضل عمل باستخدام شيء آخر غير Nokogiri له روبي الأصلي محلل SAX (ربما REXML )، أو إذا سرعة الاهتمام الخاص بك الحقيقي، تفعل جزء البحث في C / C ++ باستخدام Xerces أو ما شابه ذلك. أنا لا أعرف مدى هذه ستتعامل مع تحليل HTML بالرغم من ذلك.
وربما هذا سوف نفعل ذلك. لست متأكدا حول أداء وإذا قد يكون هناك بعض الحالات التي أنا لم أفكر في.
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')
وهذا هو بلدي حل بهم (مجد لي زميل في العمل لمساعدتي على هذا واحد!) باستخدام أسلوب عودي إلى تحليل جميع العناصر بغض النظر عن كونه أخ أو الطفل من أخ آخر.
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")
يمكنك نسخ / لصق شوط الحقيقة كما هي كبرنامج نصي روبي.
إذا كنت لا تعرف العلاقة بين العناصر، يمكنك البحث عنها بهذه الطريقة (أي مكان في المستند):
# 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
ويمكنك البحث المتحدرين من HTML::Element
Nokogiri باستخدام محددات CSS. يمكنك اجتياز الأجداد مع أسلوب .parent
.
parent_element_h1 = value.css("h1").first.parent
parent_element_h2 = value.css("h2").first.parent