: селектор эквалайзера в нокогири
В jQuery у вас есть селектор :eq, чтобы уменьшить набор сопоставляемых элементов до одного с заданным индексом.
Как то же самое можно сделать с Нокогири? Я долго искал, но так и не нашел выхода.
См. этот пример:
require 'nokogiri'
html ='
<div>
<p>foo</p></div>
<span>
<p>bar</p>
<p>foobar</p></span>
'
doc = Nokogiri::HTML(html)
p doc.search('p:eq(0)')
# -> []
3 ответа:
Вот как я ловлю рыбу:
require 'nokogiri' html =' <p>foo</p> <p>bar</p> <p>foobar</p> ' doc = Nokogiri::HTML(html) doc.search('//p[3]') => [#<Nokogiri::XML::Element:0x811058ac name="p" children=[#<Nokogiri::XML::Text:0x81104f74 "foobar">]>]
Nokogiri возвратил NodeSet, набор узлов, который действует как массив. Я могу искать с помощью
at
вместоsearch
, чтобы получить только узел:doc.at('//p[3]').text => "foobar"
Или он может вернуть набор узлов, и Вы можете попросить Ruby извлечь из него конкретный элемент:
doc.search('//p')[2].text => "foobar"
p doc.search('p:eq(0)')
'p:eq(0)'
это не CSS и не XPath. Это селектор jQuery, добавленный в JavaScript. Вы не можете использовать jQuery или JavaScript с Nokogiri в качестве метода доступа, это должен быть CSS или XPath.
Ваш пример даже не делает того, что я хочу. Оператор [i] сравним с N-м ребенком!В соответствии с документацией jQuery для
:eq
:Описание: уменьшите набор совпадающих элементов до одного с указанным индексом.
//p[3]
, или используяsearch('p')[2]
, Чтобы позволить Ruby разрезать NodeSet, вернет определенный узел. В моих примерах я извлекаю содержимое третьего тега<p>
, который является эквивалент jQuery:eq(2)
.Используя тот же пример, что и раньше:
require 'nokogiri' html =' <p>foo</p> <p>bar</p> <p>foobar</p> ' doc = Nokogiri::HTML(html) doc.search('//p[0]').text doc.search('//p[1]').text doc.search('//p[2]').text doc.search('//p[3]').text doc.search('//p')[0].text doc.search('//p')[1].text doc.search('//p')[2].text doc.search('//p')[3].text
Сбрасывая это в IRB я вижу:
irb(main):011:0* doc.search('//p[0]').text # => "" irb(main):012:0> doc.search('//p[1]').text # => "foo" irb(main):013:0> doc.search('//p[2]').text # => "bar" irb(main):014:0> doc.search('//p[3]').text # => "foobar" irb(main):015:0> doc.search('//p')[0].text # => "foo" irb(main):016:0> doc.search('//p')[1].text # => "bar" irb(main):017:0> doc.search('//p')[2].text # => "foobar" irb(main):018:0> doc.search('//p')[3].text NoMethodError: undefined method `text' for nil:NilClass from (irb):18
Использование
//p[1]
эквивалентно использованию HTML ниже с JavaScript и jQuery, загрузке его в браузер и просмотру предупреждения, которое показывает "foo":<html> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> </head> <body> <p>foo</p> <p>bar</p> <p>foobar</p> <script> $().ready(function(){ alert($('p:eq(0)').text()); }); </script> </body> </html>
Таким образом,
//p
с срезом[1]
в XPath или[0]
в Ruby эквивалентно:eq(0)
в JavaScript. Но, поскольку Nokogiri не делает JavaScript или jQuery, вы должны использовать либо CSS, либо язык XPath.
Селекторы JQuery теперь доступны, что делает возможным следующее:
require 'nokogiri' html = ' <html> <body> <p>foo</p> this text <p>bar</p> </body> </html> ' doc = Nokogiri::HTML(html) doc.at('p:contains("foo")').next_sibling.text.strip => "this text"
Похоже, что единственный способ уменьшить набор согласованных узлов-это использовать Ruby и метод массива, подобный этому:
doc = Nokogiri ... doc.search("table")[2].search("...")
Ни XPath Нокогири, ни его CSS-селекторы не поддерживают функциональ-ность, равную оператору
:eq
jQuery.
Попробуйте с
:nth-child(N)
(Первый элемент находится с N = 1)