如何使用 Nokogiri 删除重复的嵌套标签
How to remove repeated nested tags using Nokogiri
我有 HTML 嵌套重复标签:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
我想删除没有任何属性的嵌套重复 div
。结果 HTML 应如下所示:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<p>Some text</p>
</div>
</body>
</html>
如何使用 Nokogiri 或纯 Ruby 来做到这一点?
通常我不太喜欢像 Nokogiri 使用的可变结构,但在这种情况下,我认为它对你有利。这样的事情可能会奏效:
def recurse node
# depth first so we don't accidentally modify a collection while
# we're iterating through it.
node.elements.each do |child|
recurse(child)
end
# replace this element's children with it's grandchildren
# assuming it meets all the criteria
if merge_candidate?(node)
node.children = node.elements.first.children
end
end
def merge_candidate? node, name: 'div'
return false unless node.element?
return false unless node.attributes.empty?
return false unless node.name == name
return false unless node.elements.length == 1
return false unless node.elements.first.name == name
return false unless node.elements.first.attributes.empty?
true
end
[18] pry(main)> file = File.read('test.html')
[19] pry(main)> doc = Nokogiri.parse(file)
[20] pry(main)> puts doc
<!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">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
[21] pry(main)> recurse(doc)
[22] pry(main)> puts doc
<!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">
</head>
<body>
<div>
<p>Some text</p>
</div>
</body>
</html>
=> nil
[23] pry(main)>
根据您的 HTML 结构,这应该可以帮助您:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
EOT
dd = doc.at('div div').parent
dp = dd.at('div p')
dd.children.unlink
dp.parent = dd
这导致:
puts doc.to_html
# >> <!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">
# >> </head>
# >> <body>
# >> <div><p>Some text</p></div>
# >> </body>
# >> </html>
dd
是两个连续 div
标签的 parent
,换句话说,它是链中的第一个 div
。
dp
是该链末端的 p
节点。
dd.children
是一个包含 dd
的 children
的节点集,一直到并包括 dp
.
想法是将dp
,(想要的<p>
节点)嫁接到dd
,(最顶层的<div>
节点),去掉其他的干预 <div>
标签。 A NodeSet makes it easy to unlink
一次有大量标签。
阅读 at
了解为什么它对这类问题很重要。
我有 HTML 嵌套重复标签:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
我想删除没有任何属性的嵌套重复 div
。结果 HTML 应如下所示:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<p>Some text</p>
</div>
</body>
</html>
如何使用 Nokogiri 或纯 Ruby 来做到这一点?
通常我不太喜欢像 Nokogiri 使用的可变结构,但在这种情况下,我认为它对你有利。这样的事情可能会奏效:
def recurse node
# depth first so we don't accidentally modify a collection while
# we're iterating through it.
node.elements.each do |child|
recurse(child)
end
# replace this element's children with it's grandchildren
# assuming it meets all the criteria
if merge_candidate?(node)
node.children = node.elements.first.children
end
end
def merge_candidate? node, name: 'div'
return false unless node.element?
return false unless node.attributes.empty?
return false unless node.name == name
return false unless node.elements.length == 1
return false unless node.elements.first.name == name
return false unless node.elements.first.attributes.empty?
true
end
[18] pry(main)> file = File.read('test.html')
[19] pry(main)> doc = Nokogiri.parse(file)
[20] pry(main)> puts doc
<!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">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
[21] pry(main)> recurse(doc)
[22] pry(main)> puts doc
<!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">
</head>
<body>
<div>
<p>Some text</p>
</div>
</body>
</html>
=> nil
[23] pry(main)>
根据您的 HTML 结构,这应该可以帮助您:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<div>
<div>
<p>Some text</p>
</div>
</div>
</div>
</body>
</html>
EOT
dd = doc.at('div div').parent
dp = dd.at('div p')
dd.children.unlink
dp.parent = dd
这导致:
puts doc.to_html
# >> <!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">
# >> </head>
# >> <body>
# >> <div><p>Some text</p></div>
# >> </body>
# >> </html>
dd
是两个连续 div
标签的 parent
,换句话说,它是链中的第一个 div
。
dp
是该链末端的 p
节点。
dd.children
是一个包含 dd
的 children
的节点集,一直到并包括 dp
.
想法是将dp
,(想要的<p>
节点)嫁接到dd
,(最顶层的<div>
节点),去掉其他的干预 <div>
标签。 A NodeSet makes it easy to unlink
一次有大量标签。
阅读 at
了解为什么它对这类问题很重要。