2009-07-21 4 views
5

Вот пример некоторых странности:Как я могу заставить Nokogiri разобрать и вернуть XML-документ?

#!/usr/bin/ruby 

require 'rubygems' 
require 'open-uri' 
require 'nokogiri' 

print "without read: ", Nokogiri(open('http://weblog.rubyonrails.org/')).class, "\n" 
print "with read: ", Nokogiri(open('http://weblog.rubyonrails.org/').read).class, "\n" 

Запуск этого возвращается:

without read: Nokogiri::XML::Document 
with read: Nokogiri::HTML::Document 

Без XML read возвращается, и это HTML? Веб-страница определяется как «XHTML переходный», поэтому сначала я подумал Nokogiri должен быть чтение OpenURI в «Content-Type» из потока, но возвращает 'text/html':

(rdb:1) doc = open(('http://weblog.rubyonrails.org/')) 
(rdb:1) doc.content_type 
"text/html" 

который является то, что сервер возвращает , Итак, теперь я пытаюсь понять, почему Нокигири возвращает два разных значения. По-видимому, он не анализирует текст и не использует эвристику, чтобы определить, является ли контент HTML или XML.

То же самое происходит и с подачей ATOM указывает, что страницы:

(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails')) 
(rdb:1) doc.class 
Nokogiri::XML::Document 

(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails').read) 
(rdb:1) doc.class 
Nokogiri::HTML::Document 

мне нужно, чтобы быть в состоянии разобрать страницу, не зная, что это заранее, либо HTML или подача (RSS или ATOM) и надежно определить, что это такое. Я попросил Nokogiri проанализировать тело файла HTML или XML-файла, но я вижу эти несогласованные результаты.

Я думал, что я мог бы написать несколько тестов, чтобы определить тип, но тогда я побежал в XPaths не находя элементы, но регулярные поиски работают:

(rdb:1) doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails')) 
(rdb:1) doc.class 
Nokogiri::XML::Document 
(rdb:1) doc.xpath('/feed/entry').length 
0 
(rdb:1) doc.search('feed entry').length 
15 

Я понял, XPaths будет работать с XML, но результаты не также заслуживает доверия.

Эти тесты были выполнены в моем поле Ubuntu, но я видел такое же поведение на своем Macbook Pro. Мне бы хотелось узнать, что я делаю что-то неправильно, но я не видел примера для разбора и поиска, что дало мне последовательные результаты. Может ли кто-нибудь показать мне ошибку моих путей?

+0

Как это ни парадоксально, это не вопрос ... – 2010-12-10 06:49:50

ответ

12

Это связано с тем, как работает Nokogiri parse method. Вот источник:

# File lib/nokogiri.rb, line 55 
    def parse string, url = nil, encoding = nil, options = nil 
     doc = 
     if string =~ /^\s*<[^Hh>]*html/i # Probably html 
      Nokogiri::HTML::Document.parse(string, url, encoding, options || XML::ParseOptions::DEFAULT_HTML) 
     else 
      Nokogiri::XML::Document.parse(string, url, encoding, options || XML::ParseOptions::DEFAULT_XML) 
     end 
     yield doc if block_given? 
     doc 
    end 

Ключ линия if string =~ /^\s*<[^Hh>]*html/i # Probably html. Когда вы просто используете open, он возвращает объект, который не работает с регулярным выражением, поэтому он всегда возвращает false. С другой стороны, read возвращает строку, поэтому может считаться HTML. В этом случае это так, потому что оно соответствует этому регулярному выражению. Вот начало этой строки:

<!DOCTYPE html PUBLIC 

Регулярное выражение соответствует «DOCTYPE!», Чтобы [^Hh>]*, а затем соответствует «HTML», таким образом, предполагая, что это HTML. Почему кто-то выбрал это регулярное выражение, чтобы определить, является ли файл HTML вне меня. С этим регулярным выражением файл, начинающийся с тега, например <definitely-not-html>, считается HTML, но <this-is-still-not-html> считается XML. Вам, вероятно, лучше всего держаться подальше от этой немой функции и напрямую ссылаться на Nokogiri::HTML::Document#parse или Nokogiri::XML::Document#parse.

+0

Ah. И ух. Да, это очень легко обмануть. Чтобы обойти это, я написал несколько методов для обоих типов документов, которые выполняют некоторые тесты для «/ html/head» и тегов для RSS и ATOM, и они, похоже, надежно хранят документы HTML, RSS и ATOM. Я разбираю документ как HTML :: Document, так и XML :: Document, но не люблю это делать. Я как бы думаю, что Hpricot оценивает точку, потому что у нее только один тип документа. Теперь, почему поиск «.xpath ('/ feed/entry') завершился неудачно, но« .search (запись в фиде) »будет успешным в документе Nokogiri :: XML :: Document? Это тоже меня раздражает, потому что это не так, t44). –

+3

Технически селектор CSS 'feed entry' не эквивалентен XPath'/feed/entry'. Эквивалентный XPath является '// feed // entry'. В случае Atom ваш оригинальный XPath Исправлена ​​проблема с тем, что вы должны включать пространства имен. Попробуйте следующее: '/ xmlns: feed/xmlns: entry' – Pesto

+0

Спасибо, Песто, вы были очень полезны! –

5

В ответ на эту часть вопроса:

Я думал, что я мог бы написать несколько тестов, чтобы определить тип, но потом я наткнулся на XPaths не находя элементы, но регулярные поиски работы:

Я только что наткнулся на эту проблему, используя nokogiri для синтаксического анализа подачи атома. Проблема, казалось, вплоть до анонимного имя пространства декларации:

<feed xmlns="http://www.w3.org/2005/Atom"> 

Удаление декларации Xmlns из исходного XML позволит Nokogiri искать с XPath как обычно. Удаление этой декларации из фида явно не было вариантом, поэтому вместо этого я просто удалил пространства имен из документа после разбора. например:

doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails')) 
doc.remove_namespaces! 
doc.xpath('/feed/entry').length 

Уродство Я знаю, но это было трюк.

+3

+1 для метода remove_namespaces !. Я никогда не знал этого, и ваш комментарий спас мне огромное количество времени. – rhh

+0

Сайт Nokogiri упоминает об этом, с оговоркой, что вы должны знать, что нет коллизии betw een, или, если есть столкновения, вам все равно. –

Смежные вопросы