代码之家  ›  专栏  ›  技术社区  ›  the Tin Man

如何让Nokogiri解析并返回XML文档?

  •  5
  • the Tin Man  · 技术社区  · 15 年前

    以下是一些奇怪的例子:

    #!/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设备上完成的,但是我在我的MacBookPro上看到了同样的行为。我很想知道我做错了什么,但我还没有看到一个解析和搜索的示例,它给出了一致的结果。有人能告诉我我的错误吗?

    2 回复  |  直到 11 年前
        1
  •  13
  •   Pesto    15 年前

    这和Nokogiri的方式有关 parse method 作品。资料来源如下:

    # 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。在这种情况下,它是,因为它与regex匹配。这是字符串的开头:

    <!DOCTYPE html PUBLIC
    

    正则表达式与“!向“DOCTYPE” [^Hh>]* 然后匹配“html”,因此假设它是html。为什么有人选择这个regex来确定文件是否是HTML超出了我的能力。使用这个regex,一个以一个标签开始的文件 <definitely-not-html> 被认为是HTML,但是 <this-is-still-not-html> 被认为是XML。您最好远离这个愚蠢的函数并调用 Nokogiri::HTML::Document#parse Nokogiri::XML::Document#parse 直接。

        2
  •  5
  •   Will    15 年前

    回答问题的这一部分:

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

    我知道很难看,但它起了作用。