我是F#的新手并且正在使用并正在处理一些HTML解析代码。我想从HTML文档中删除符合某些条件的元素。在这里,我有一系列对象(HtmlNodes),并希望从文档中删除它们。
这是使用模式匹配的惯用方法吗?此外,由于HtmlNode.Remove()对原始HtmlDocument对象有副作用,是否有任何特定的方法来构造代码以使副作用明显或如何处理。使用代码,你可以像你一样迂腐。
open HtmlAgilityPack
let removeNodes (node : HtmlNode) =
let (|HiddenNodeCount|) (n : HtmlNode) =
match n.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
| null -> 0
| _ as x -> Seq.length x
match node with
| x when x.Name.ToLower() = "script" -> node.Remove()
| x when x.NodeType = HtmlNodeType.Comment -> node.Remove()
| HiddenNodeCount x when x > 0 -> node.Remove()
| _ -> ()
let html = "some long messy html code would be here"
let dom = new HtmlDocument(OptionAutoCloseOnEnd=true)
dom.LoadHtml(html)
let nodes = dom.DocumentNode.DescendantNodes()
nodes |> Seq.toArray |> Array.iter removeNodes
答案 0 :(得分:1)
就个人而言,当您没有要分解的数据结构时,我更喜欢if elif else
而不是模式匹配(它只是更少的输入,也可以用来区分结构何时被分解而不是简单的案例测试)
您的代码中有一些奇怪的东西。 Active Pattern在这里不是很有用,原因有二:首先,它的范围仅限于removeNodes,所以它只使用一次。我将在稍后讨论第二个问题,但首先我将展示如何通过消除活动模式来编写它,至少对我来说,使副作用更明显(通过分离测试节点是否应该是的代码)从执行删除的代码中删除了:
let shouldRemoveNode (node : HtmlNode) =
if node.Name.ToLower() = "script" then true
elif node.NodeType = HtmlNodeType.Comment then true
else match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
| null -> false
| x -> Seq.length x > 0
let removeNode (node: HtmlNode) =
if shouldRemoveNode(node) then node.Remove() else ()
注意我在可见性隐藏查询中使用了模式匹配,因为我确实要匹配null并绑定到x,否则(而不是绑定到x,然后使用if else
测试x)。
Active Pattern的第二个奇怪之处在于,您正在使用它将节点转换为int,但是您获得的长度并不是立即有用(您仍然需要对它执行测试)。而在这里更强大的使用活动模式将是将节点划分为不同类型(假设这不是临时的,这可能是第一点)。所以你可以:
//expand to encompass several other kinds of nodes
let (|Script|Comment|Hidden|Other|) (node : HtmlNode) =
if node.Name.ToLower() = "script" then Script
elif node.NodeType = HtmlNodeType.Comment then Comment
else match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
| null -> Other
| x -> if Seq.length x > 0 then Hidden
else Other
let removeNode (node: HtmlNode) =
match node with
| Script | Comment | Hidden -> node.Remove()
| Other -> ()
修改强>
@Pascal在评论中观察到shouldRemoveNode
可以进一步浓缩为一个大的布尔表达式:
let shouldRemoveNode (node : HtmlNode) =
node.Name.ToLower() = "script" ||
node.NodeType = HtmlNodeType.Comment ||
match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
| null -> false
| x -> Seq.length x > 0
答案 1 :(得分:0)
我不清楚这比使用函数和if-then-else更好,例如
let HiddenNodeCount (n : HtmlNode) =
match n.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
| null -> 0
| x -> Seq.length x
if node.Name.ToLower() = "script" then
node.Remove()
elif node.NodeType = HtmlNodeType.Comment then
node.Remove()
elif HiddenNodeCount node > 0 then
node.Remove()