如何让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 

没有read返回XML,并且它是HTML? 网页被定义为“XHTML过渡”,所以起初我认为Nokogiri必须从流中读取OpenURI的“内容类型”,但返回'text/html'

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

这是服务器返回的内容。 所以,现在我想弄清楚为什么Nokogiri会返回两个不同的值。 它似乎不是解析文本并使用启发式方法来确定内容是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 feed文件的正文,但我看到了那些不一致的结果。

我以为我可以编写一些测试来确定类型,但后来我遇到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 

我认为xpath可以使用XML,但结果看起来也不值得信赖。

这些测试都是在我的Ubuntu盒子上完成的,但我在Macbook Pro上看到过相同的行为。 我很想知道我做错了什么,但我还没有看到解析和搜索的例子,这给了我一致的结果。 谁能告诉我我的方式错误?

它与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 ,它会返回一个不能与regex一起使用的对象,因此它总是返回false。 另一方面, read返回一个字符串,因此可以将其视为HTML。 在这种情况下,它是,因为它匹配该正则表达式。 这是该字符串的开头:

  

正则表达式将“!DOCTYPE”与[^Hh>]*匹配,然后匹配“html”,从而假设它是HTML。 为什么有人选择这个正则表达式来确定文件是否是HTML是超出我的。 使用此正则表达式,以类的标记开头的文件被视为HTML,但被视为XML。 你可能最好远离这个愚蠢的function并直接调用Nokogiri::HTML::Document#parseNokogiri::XML::Document#parse

回答你的这部分问题:

我以为我可以编写一些测试来确定类型,但后来我遇到xpaths没有找到元素,但常规搜索工作:

我刚刚使用nokogiri来解析primefaces进给问题。 问题似乎取决于匿名名称空间声明:

  

从源xml中删除xmlns声明将使Nokogiri能够按照惯例搜索xpath。 从feed中删除该声明显然不是一个选项,所以我只是在解析后从文档中删除了名称空间。 例如:

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

丑陋我知道,但它做到了伎俩。