2009-07-21 10 views
5

He aquí una muestra de algunas rarezas:¿Cómo puedo obtener Nokogiri para analizar y devolver un documento 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" 

La ejecución de este regresa:

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

Sin el XML read rendimientos, y con ella es HTML? La página web se define como "XHTML de transición", por lo que en un principio pensé Nokogiri debe haber sido la lectura de "Content-Type" de OpenURI de la corriente, pero que devuelve 'text/html':

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

que es lo que el servidor está volviendo . Entonces, ahora estoy tratando de descubrir por qué Nokogiri está devolviendo dos valores diferentes. No parece analizar el texto y usar la heurística para determinar si el contenido es HTML o XML.

Lo mismo está sucediendo con el canal de información ATOM apuntado por esa página:

(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 

tengo que ser capaz de analizar una página sin saber lo que es por adelantado, ya sea HTML o una alimentación (RSS o ATOM) y determinar de manera confiable cuál es. Le pedí a Nokogiri que analizara el cuerpo de un archivo de fuente HTML o XML, pero veo esos resultados inconsistentes.

pensé que podría escribir algunas pruebas para determinar el tipo, pero luego me encontré con XPaths no encontrar elementos, pero las búsquedas regulares de trabajo:

(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 

que pensé XPaths sería trabajar con XML, pero los resultados no lo hacen parece confiable tampoco.

Estas pruebas se realizaron todas en mi caja de Ubuntu, pero he visto el mismo comportamiento en mi Macbook Pro. Me encantaría descubrir que estoy haciendo algo mal, pero no he visto un ejemplo de análisis y búsqueda que me haya dado resultados consistentes. ¿Alguien puede mostrarme el error de mis caminos?

+0

Irónicamente esto en realidad NO ES una pregunta ... –

Respuesta

12

Tiene que ver con la forma en que trabaja Nokogiri's parse method. Aquí está la fuente:

# 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 

La clave es la línea if string =~ /^\s*<[^Hh>]*html/i # Probably html. Cuando solo usa open, devuelve un objeto que no funciona con expresiones regulares, por lo que siempre devuelve falso. Por otro lado, read devuelve una cadena, por lo que podría considerarse como HTML. En este caso lo es, porque coincide con esa expresión regular. Aquí está el comienzo de esa cadena:

<!DOCTYPE html PUBLIC 

La expresión coincide con el "! DOCTYPE" a [^Hh>]* y luego coincide con el "html", asumiendo así que es HTML. Por qué alguien seleccionó esta expresión regular para determinar si el archivo es HTML me supera. Con esta expresión regular, un archivo que comienza con una etiqueta como <definitely-not-html> se considera HTML, pero <this-is-still-not-html> se considera XML. Probablemente sea mejor que se mantenga alejado de esta función estúpida e invocando directamente al Nokogiri::HTML::Document#parse o al Nokogiri::XML::Document#parse.

+0

Ah. Y Ugh. Sí, es muy fácil de engañar. Para solucionarlo escribí algunos métodos para ambos tipos de documento que hacen algunas pruebas para "/ html/head" y las etiquetas para RSS y ATOM y parecen atrapar de manera confiable los documentos HTML, RSS y ATOM. Sin embargo, estoy analizando un documento como HTML :: Document y XML :: Document, y no me gusta tener que hacer eso. Creo que Hpricot puntúa un punto porque solo tiene un tipo de documento. Ahora, ¿por qué falla una búsqueda ".xpath ('/ feed/entry') pero" .search (entrada de entrada) "tendrá éxito en un documento Nokogiri :: XML :: Eso me está volviendo loco porque no lo hace Parece coherente. –

+3

Técnicamente, el selector de CSS 'feed entry' no es equivalente al XPath'/feed/entry'. El XPath equivalente es '// feed // entry'. En el caso de Atom, su XPath original es correcto, sin embargo, su problema es que debe incluir los espacios de nombres. Pruebe esto: '/ xmlns: feed/xmlns: entrada' – Pesto

+0

Gracias Pesto, ¡ha sido muy útil! –

5

En respuesta a esta parte de su pregunta:

pensé que podría escribir algunas pruebas para determinar el tipo, pero luego me encontré con XPaths no encontrar elementos, pero búsquedas regulares de trabajo:

Acabo de encontrarme con este problema al usar nokogiri para analizar un feed de átomos. El problema parecía a la declaración de espacio de nombres en el anonimato:

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

Extracción de la declaración xmlns desde el XML de origen permitiría Nokogiri a buscar con XPath como de costumbre. La eliminación de esa declaración del feed obviamente no era una opción aquí, así que en su lugar acabo de eliminar los espacios de nombres del documento después del análisis. por ejemplo:

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

Ugly Lo sé, pero funcionó.

+3

+1 para el método remove_namespaces! Nunca supe eso y tu comentario me ahorró muchísimo tiempo. – rhh

+0

El sitio de Nokogiri menciona hacer eso, con la advertencia de que no se debe colisiones entre ellos een etiquetas, o, si hay colisiones, no te importa. –

Cuestiones relacionadas