Como posso obter Nokogiri para analisar e retornar um documento XML?
-
18-09-2019 - |
Pergunta
Aqui está uma amostra de alguns estranheza:
#!/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"
A execução deste retornos:
without read: Nokogiri::XML::Document
with read: Nokogiri::HTML::Document
Sem o XML read
retorna, e com ele é HTML? A página web é definida como "XHTML de transição", então no começo eu pensei Nokogiri deve ter sido a leitura "do tipo de conteúdo" do OpenURI do fluxo, mas que retorna 'text/html'
:
(rdb:1) doc = open(('http://weblog.rubyonrails.org/'))
(rdb:1) doc.content_type
"text/html"
que é o que o servidor está retornando. Então, agora eu estou tentando descobrir por que Nokogiri está retornando dois valores diferentes. Ele não parece ser analisar o texto e usando a heurística para determinar se o conteúdo é HTML ou XML.
A mesma coisa está acontecendo com o feed ATOM apontado por essa página:
(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
Eu preciso ser capaz de analisar uma página sem saber o que é de antecedência, HTML ou um feed (RSS ou Atom) e determinar com certeza qual é. Perguntei Nokogiri para analisar o corpo de qualquer um HTML ou arquivo de feed XML, mas eu estou vendo esses resultados inconsistentes.
Eu pensei que eu poderia escrever alguns testes para determinar o tipo, mas depois eu corri para XPaths não encontrar elementos, mas pesquisas regulares de trabalho:
(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
Eu XPaths figuradas iria trabalhar com XML, mas os resultados não parecem confiáveis ??também.
Estes testes foram todos feitos na minha caixa de Ubuntu, mas eu vi o mesmo comportamento no meu Macbook Pro. Eu adoraria descobrir o que estou fazendo algo errado, mas eu não vi um exemplo para a análise e pesquisa que me deu resultados consistentes. Alguém pode me mostrar o erro dos meus caminhos?
Solução
Tem a ver com a maneira href="http://nokogiri.rubyforge.org/nokogiri/Nokogiri.html#M000355" rel="noreferrer"> método de Nokogiri obras. Aqui está a fonte:
# 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
A chave é a if string =~ /^\s*<[^Hh>]*html/i # Probably html
line. Quando você usar apenas open
, ele retorna um objeto que não funciona com regex, assim ele sempre retorna false. Por outro lado, read
retorna uma string, por isso pode ser considerado como HTML. Neste caso, ele é, porque ele corresponde regex. Aqui está o início dessa cadeia:
<!DOCTYPE html PUBLIC
O regex corresponde ao "! DOCTYPE" para [^Hh>]*
e, em seguida, combina com o "html", assumindo-se assim de HTML. Por que alguém selecionado este regex para determinar se o arquivo é HTML está além de mim. Com este regex, um arquivo que começa com uma tag como <definitely-not-html>
é considerado HTML, mas <this-is-still-not-html>
é considerado XML. Você provavelmente está melhor fora ficar longe de esta função mudo e invocando Nokogiri::HTML::Document#parse
ou Nokogiri::XML::Document#parse
diretamente.
Outras dicas
Em resposta a esta parte da sua pergunta:
Eu pensei que eu poderia escrever alguns testes para determinar o tipo, mas depois eu corri para XPaths não encontrar elementos, mas pesquisas regulares de trabalho:
Acabei de se deparar com este problema utilizando nokogiri para analisar um feed Atom. O problema parecia até a declaração de nome-espaço anônimo:
<feed xmlns="http://www.w3.org/2005/Atom">
Removendo a declaração xmlns do xml fonte permitiria Nokogiri de pesquisa com o XPath como de costume. Removendo essa declaração a partir da alimentação, obviamente, não era uma opção aqui, então ao invés eu só removeu os namespaces do documento após a análise. por exemplo:
doc = Nokogiri.parse(open('http://feeds.feedburner.com/RidingRails'))
doc.remove_namespaces!
doc.xpath('/feed/entry').length
feio eu sei, mas fez o truque.