2009-07-21 3 views
5

Voici un échantillon de quelques bizarreries:Comment puis-je faire en sorte que Nokogiri analyse et renvoie un document 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" 

L'exécution de ce retour:

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

Sans XML read de retour, et il est HTML? La page Web est définie comme « XHTML transition », donc au début je pensais Nokogiri doit avoir été lu « type de contenu » de openURI du flux, mais qui retourne 'text/html':

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

qui est ce que le serveur retourne . Donc, maintenant j'essaie de comprendre pourquoi Nokogiri retourne deux valeurs différentes. Il ne semble pas analyser le texte et utiliser des heuristiques pour déterminer si le contenu est HTML ou XML.

La même chose se produit avec l'ATOM pointée par cette page:

(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 

je dois être capable d'analyser une page sans savoir ce qu'il est en avance, soit en HTML ou un flux (RSS ou ATOM) et détermine de manière fiable de quoi il s'agit. J'ai demandé à Nokogiri d'analyser le corps d'un fichier de flux HTML ou XML, mais je vois ces résultats incohérents.

Je pensais que je pourrais écrire des tests pour déterminer le type mais je suis tombé sur XPath ne pas trouver des éléments, mais des recherches régulières de travail:

(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 

je me suis dit XPath travaillerait avec XML, mais les résultats ne avoir l'air digne de confiance non plus.

Ces tests ont tous été effectués sur ma boîte Ubuntu, mais j'ai vu le même comportement sur mon Macbook Pro. J'aimerais découvrir que je fais quelque chose de mal, mais je n'ai pas vu un exemple pour l'analyse et la recherche qui m'a donné des résultats cohérents. Quelqu'un peut-il me montrer l'erreur de mes manières?

+0

Ironiquement, ce n'est PAS une question ... –

Répondre

12

Cela a à voir avec la façon dont le parse method de Nokogiri fonctionne. Voici la source:

# 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 clé est la ligne if string =~ /^\s*<[^Hh>]*html/i # Probably html. Lorsque vous utilisez simplement open, il renvoie un objet qui ne fonctionne pas avec regex, donc il retourne toujours false. D'autre part, read renvoie une chaîne de caractères, de sorte que peut être considéré comme comme HTML. Dans ce cas, il l'est, car il correspond à cette regex. Voici le début de cette chaîne:

<!DOCTYPE html PUBLIC 

Le regex correspond à la « DOCTYPE » à [^Hh>]* et correspond alors le « html », assumant ainsi son HTML. Pourquoi quelqu'un a choisi cette regex pour déterminer si le fichier est HTML est au-delà de moi. Avec cette regex, un fichier qui commence par une balise comme <definitely-not-html> est considéré comme HTML, mais <this-is-still-not-html> est considéré comme XML. Vous êtes probablement mieux de rester loin de cette fonction stupide et d'appeler Nokogiri::HTML::Document#parse ou Nokogiri::XML::Document#parse directement.

+0

Ah. Et Ugh. Oui, c'est très facilement berner. Pour contourner ce problème, j'ai écrit quelques méthodes pour les deux types de document qui font des tests pour "/ html/head" et les balises pour RSS et ATOM et ils semblent attraper les documents HTML, RSS et ATOM de manière fiable. Je suis en train d'analyser un document en tant que HTML :: Document et XML :: Document si, et n'aime pas avoir à faire cela. Je pense que Hpricot marque un point parce qu'il n'a qu'un seul type de document. Maintenant, pourquoi une recherche ".xpath ('/ feed/entry') échoue mais" .search (feed entry) "réussira sur un Nokogiri :: XML :: Document? Cela me rend dingue aussi parce qu'il doesn ' –

+3

Techniquement, le sélecteur CSS 'feed entry' n'est pas équivalent au XPath'/feed/entry', le XPath équivalent '// feed // entry' Dans le cas d'Atom, votre XPath d'origine est Votre problème est que vous devez inclure les espaces de noms Essayez ceci: '/ xmlns: feed/xmlns: entry' – Pesto

+0

Merci Pesto, vous avez été très utile! –

5

En réponse à cette partie de votre question:

Je pensais que je pourrais écrire des tests pour déterminer le type, mais je suis tombé sur XPath ne pas trouver des éléments, mais recherches régulières de travail:

Je viens de rencontrer ce problème en utilisant nokogiri pour analyser un flux d'atomes. Le problème semblait jusqu'à la déclaration espace de nom anonyme:

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

Retrait de la déclaration xmlns du xml source permettrait Nokogiri de recherche avec XPath comme d'habitude. Supprimer cette déclaration du flux n'était évidemment pas une option ici, donc à la place j'ai juste enlevé les espaces de noms du document après l'analyse. par exemple:

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

Ugly Je sais, mais il a fait l'affaire.

+3

+1 pour la méthode remove_namespaces! Je n'ai jamais su cela et votre commentaire m'a fait gagner énormément de temps – rhh

+0

Le site de Nokogiri mentionne cela, avec la mise en garde que vous devez savoir il n'y a pas collisions entre een tags, ou, s'il y a des collisions, vous vous en fichez. –

Questions connexes