5

这是一些奇怪的示例:

#!/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 提要文件的正文,但我看到了这些不一致的结果。

我以为我可以编写一些测试来确定类型,但后来我遇到了 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 上看到了相同的行为。我很想知道我做错了什么,但我还没有看到一个解析和搜索的例子,它给了我一致的结果。谁能告诉我我的方式的错误?

4

2 回答 2

13

它与 Nokogiri 的parse 方法的工作方式有关。这是来源:

# 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

于 2009-07-21T13:26:23.110 回答
5

回答您问题的这一部分:

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

我刚刚使用 Nokogiri 解析 Atom 提要遇到了这个问题。问题似乎归结为匿名命名空间声明:

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

从源 XML 中删除 XMLNS 声明将使 Nokogiri 能够像往常一样使用 XPath 进行搜索。从提要中删除该声明显然不是这里的选项,因此我只是在解析后从文档中删除了命名空间:

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

我知道丑陋,但它成功了。

于 2010-06-10T14:45:31.133 回答