了解像 Nokogiri 这样的解析器是如何工作的很重要。
为了帮助您,它会尝试修复损坏/格式错误的 HTML 或 XML。您的 HTML 格式不正确,因此将在 Nokogiri 解析它时对其进行修复,但是,该过程可能会使 Nokogiri 进一步破坏 HTML。为了避免这种情况,我们有时必须在将内容交给 Nokogiri 之前对其进行预处理,或者我们必须在之后通过替换节点来解开它。
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<p> Hello World, ""How are you today""
<a href=""www.hello.comm"">Hello</a>
etc.
</p>
EOT
这会将 HTML 解析为 DOM。
doc.at('p').to_html
# => "<p> Hello World, \"\"How are you today\"\"\n<a href=\"\" www.hello.comm>Hello</a>\netc.\n</p>"
""How are you today""
由于它是一个文本节点,因此处理文本时没有进行任何修改:
doc.at('p').child.class # => Nokogiri::XML::Text
doc.at('p').child.content # => " Hello World, \"\"How are you today\"\"\n"
解析后很容易解决:
doc.at('p').child.content = doc.at('p').child.content.gsub('""', '"')
# => " Hello World, \"How are you today\"\n"
尝试修复<a>
标签的参数是一个完全不同的故事,因为到那时,Nokogiri 已经修复了双引号,导致标记错误:
doc.at('a').to_html
# => "<a href=\"\" www.hello.comm>Hello</a>"
请注意,www.hello.comm
已在其包含的引号之外进行了提升。
要解决此问题,需要在将 HTML 交给 Nokogiri之前进行一些预处理,或者修复节点并用修复的节点替换损坏的节点。
<a>
以下是标签预处理的基础:
html = <<EOT
<p> Hello World, ""How are you today""
<a href=""www.hello.comm"">Hello</a>
etc.
</p>
EOT
html.gsub(/href=""([^"]+)""/, 'href="\1"')
# => "<p> Hello World, \"\"How are you today\"\"\n<a href=\"www.hello.comm\">Hello</a>\netc.\n</p>\n"
如果你走那条路,不要幻想。编写小的、原子的更改,以避免在 HTML 更改时破坏您的模式。
一种更健壮的方法(“健壮”比我们通常使用解析器得到的要少)是:
bad_a = doc.at('a')
fixed_a = bad_a.to_html.gsub(/""\s([^>]+)>/, '"\1">')
bad_a.replace(fixed_a)
doc.at('p')
# => #(Element:0x3fe4ce9de9e4 {
# name = "p",
# children = [
# #(Text " Hello World, \"How are you today\"\n"),
# #(Element:0x3fe4ce9e0fdc {
# name = "a",
# attributes = [
# #(Attr:0x3fe4ce9e0fa0 {
# name = "href",
# value = "www.hello.comm"
# })],
# children = [ #(Text "Hello")]
# }),
# #(Text "\netc.\n")]
# })
doc.at('p').to_html
# => "<p> Hello World, \"How are you today\"\n<a href=\"www.hello.comm\">Hello</a>\netc.\n</p>"
可以使用毯子gsub
来按摩文本,但在大型/复杂文档中存在附带损坏的高风险。想象一下,如果一个文件会发生什么
html.gsub('""', '"')
当有许多包含空字符串的标签时使用,例如:
<input value="" name="foo"><input value="" name="bar">
搜索/替换的结果将是:
<input value=" name="foo"><input value=" name="bar">
这几乎没有改善事情,反而会进一步严重破坏文档。
相反,最好通过手术解决问题。回到黑暗的、早期的网络先驱时代,我们曾经看到大量格式不正确的内容,而必须使用正则表达式对其进行处理是正常的攻击计划。现在,有了解析器,我们通常可以避免它,并且可以隔离问题并有选择地修复我们想要的。查看执行此操作所需的代码表明,将其做好并不需要太多。