Как заставить Нокогири анализировать и возвращать XML-документ?

StackOverflow https://stackoverflow.com/questions/1157138

Вопрос

Вот пример какой-то странности:

#!/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

Без read возвращает XML, а вместе с ним и HTML?Веб-страница определена как «переходная XHTML», поэтому сначала я подумал, что Нокогири, должно быть, читал «тип контента» OpenURI из потока, но это возвращает '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), и надежно определять, что это такое.Я попросил Нокогири проанализировать тело файла фида 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.Мне бы очень хотелось узнать, что я делаю что-то не так, но я не видел примера синтаксического анализа и поиска, который дал бы мне последовательные результаты.Может ли кто-нибудь показать мне ошибку моего пути?

Это было полезно?

Решение

Это связано с тем, как Нокогири метод анализа работает.Вот источник:

# 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 напрямую.

Другие советы

Отвечая на эту часть вашего вопроса:

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

Я только что столкнулся с этой проблемой, используя nokogiri для анализа потока атомов.Проблема, похоже, сводилась к анонимному объявлению пространства имен:

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

Удаление объявления xmlns из исходного xml позволит Нокогири выполнять поиск с помощью xpath, как обычно.Удаление этого объявления из фида, очевидно, здесь не вариант, поэтому вместо этого я просто удалил пространства имен из документа после анализа.например:

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

Я знаю, это некрасиво, но это сработало.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top