Как я могу заставить Nokogiri разобрать и вернуть XML-документ? - PullRequest
5 голосов
/ 21 июля 2009

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

#!/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), и надежно определять, что это такое. Я попросил 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. Я хотел бы узнать, что я делаю что-то не так, но я не видел пример анализа и поиска, который дал бы мне последовательные результаты. Может кто-нибудь показать мне ошибку моих путей?

Ответы [ 2 ]

13 голосов
/ 21 июля 2009

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

# 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.

5 голосов
/ 10 июня 2010

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

Я думал, что смогу написать несколько тестов определить тип, но потом я столкнулся с 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

Ужасно, я знаю, но это помогло.

...