سؤال

وأنا أحاول ملء 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top