如何让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#parse
或Nokogiri::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
丑陋我知道,但它做到了伎俩。