阻止Nokogiri添加DOCTYPE和meta标签?

时间:2014-09-23 15:19:28

标签: html ruby nokogiri

我正在尝试使用Nokogiri将一些模板文件从一种格式转换为另一种格式。但它不断添加标签。我试图阻止它添加Doctype和meta标签,但无法弄明白。我试过了

@doc = Nokogiri::HTML.parse(r)

但是添加了标签。我也试过了

@doc = Nokogiri::HTML.fragment(r)

按照“How to prevent Nokogiri from adding <DOCTYPE> tags?”中的建议,但删除了文档中的所有<html><head><body>标记。

如果重要,我的阅读文件的代码是:

f = File.read(infile)
r = f.gsub(/<tmpl_var ([^>]*)>/, '{{{\1}}}')
@doc = Nokogiri::HTML.fragment(r)

我需要事先做gsub,因为我需要替换不正确HTML的<tmpl_var>标记并导致更多问题。

使用HTML.fragment(r)时,我会收到htmlParseStartTag: misplaced <html> tag错误(以及<body><head>的类似错误。)

有没有办法阻止它进行这些添加?

转换示例:

在:

<html>
    <head>
        <script>
            var x = "y";
        </script>
    </head>
    <body>
        <div>
            Stuff
        </div>
   </body>
</html>

使用Parse之后:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script>
            var x = "y";
        </script>
    </head>
    <body>
        <div>
            Stuff
        </div>
    </body>
</html>

使用HTML.fragmentHTML::DocumentFragment.parse后:

<script>
    var x = "y";
</script>

<div>
    Stuff
</div>

在这种情况下,我希望它只输出之前的部分。 (在真实的剧本中,我做了一些改变)。

2 个答案:

答案 0 :(得分:3)

可以告诉Nokogiri 添加标准HTML标头。考虑这些:

require 'nokogiri'

doc = Nokogiri::HTML('<p>foo</p>')
doc.to_html # => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>foo</p></body></html>\n"

doc = Nokogiri::HTML.fragment('<p>foo</p>')
doc.to_html # => "<p>foo</p>"

tmpl_var是HTML中的错误标记名称,{{{\1}}}也是如此,因此要求Nokogiri尝试解析其中一个会导致出现问题:

doc = Nokogiri::HTML.fragment('<templ_var p1="baz">foo</templ_var>')
doc.errors # => [#<Nokogiri::XML::SyntaxError: Tag templ_var invalid>]

但你仍然可以使用DOM:

doc.to_html # => "<templ_var p1=\"baz\">foo</templ_var>"
doc.search('templ_var').each { |t| t.name = 'bar'}
doc.to_html # => "<bar p1=\"baz\">foo</bar>"

或者:

doc.to_html # => "<div><templ_var p1=\"baz\">foo</templ_var></div>"
doc.search('templ_var').each { |t| t.replace('{{{\1}}}') }
doc.to_html # => "<div>{{{\\1}}}</div>"

将这些东西放在一起,再加上一点诡计:

doc = Nokogiri::HTML.fragment('<div><templ_var p1="baz">foo</templ_var></div>')

doc.to_html # => "<div><templ_var p1=\"baz\">foo</templ_var></div>"

doc.search('templ_var').each { |t| t.replace('{{{\1}}}') }
doc.to_html # => "<div>{{{\\1}}}</div>"

header = Nokogiri::XML.fragment('<html><body>')
header.at('body').children = doc
header.to_html # => "<html><body><div>{{{\\1}}}</div></body></html>"

所以,我会追求这样的事情。

现在,为什么是Nokogiri在解析片段时剥离<html>标签?我不知道。如果<body><head>丢失,则会<html>单独留下:

Nokogiri::HTML.fragment('<p>foo<p>').to_html 
# => "<p>foo</p><p></p>"
Nokogiri::HTML.fragment('<body><p>foo<p></body>').to_html 
# => "<body>\n<p>foo</p>\n<p></p>\n</body>"

但如果存在<head><html>,则会变得很时髦:

Nokogiri::HTML.fragment('<head><style></style></head><body><p>foo<p></body>').to_html 
# => "<style></style><p>foo</p><p></p>"
Nokogiri::HTML.fragment('<html><head><style></style></head><body><p>foo<p></body></html>').to_html 
# => "<style></style><p>foo</p><p></p>"

这对我来说就像是Nokogiri的一个臭虫,因为我还没有看到任何记录这种行为的东西。

答案 1 :(得分:2)

您可以使用Nokogiri::XML::DocumentFragment代替Nokogiri::HTML::DocumentFragment来解决此问题。 XML版本不会删除html,head或body标签。