以下是一些奇怪的例子:
#!/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上看到过相同的行为。我很想知道我做错了什么,但我没有看到解析和搜索的例子,这给了我一致的结果。任何人都可以向我展示我的方式错误吗?
答案 0 :(得分:13)
这与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
时,它返回一个不能与正则表达式一起使用的对象,因此它总是返回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
。
答案 1 :(得分:5)
回答你问题的这一部分:
我以为我可以写一些测试 确定类型,但后来我遇到了 xpath没有找到元素,但是 定期搜索工作:
我刚刚使用nokogiri解析原子提要来解决这个问题。问题似乎取决于匿名名称空间声明:
<feed xmlns="http://www.w3.org/2005/Atom">
从源xml中删除xmlns声明将使Nokogiri能够按常规搜索xpath。从feed中删除该声明显然不是一个选项,所以我只是在解析后从文档中删除了名称空间。例如:
doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails'))
doc.remove_namespaces!
doc.xpath('/feed/entry').length
丑陋我知道,但它确实有效。