haskell - 通过镜头过滤树中的内部元素

标签 haskell haskell-lens

我一直承认自己镜头不好,但是通过实例学习不是一件好事吗?我想获取 HTML,使用 taggy-lens 解析它,然后从内部删除所有 script 元素。这是我的尝试:

#!/usr/bin/env stack
-- stack --resolver lts-7.1 --install-ghc runghc --package text --package lens --package taggy-lens --package string-class --package classy-prelude

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}

import ClassyPrelude
import Control.Lens hiding (children, element)
import Data.String.Class (toText, fromText, toString)
import Data.Text (Text)
import Text.Taggy.Lens
import qualified Text.Taggy.Lens as Taggy
import qualified Text.Taggy.Renderer as Renderer

somehtmlSmall :: Text
somehtmlSmall =
    "<!doctype html><html><body>\
    \<div id=\"article\"><div>first</div><div>second</div><script>this should be removed</script><div>third</div></div>\
    \</body></html>"

renderWithoutScriptTag :: Text
renderWithoutScriptTag =
    let mArticle :: Maybe Taggy.Element
        mArticle =
            (fromText somehtmlSmall) ^? html .
            allAttributed (ix "id" . only "article")
        mArticleFiltered =
            fmap
                (\el ->
                      el ^.. to universe . traverse .
                      filtered (\n -> n ^. name /= "script"))
                mArticle
    in maybe "" (toText . concatMap Renderer.render) mArticleFiltered

main :: IO ()
main = print renderWithoutScriptTag

将此文件标记为可执行文件并运行它,您将看到:

➜  tmp  ./scraping-question.hs
"<div id=\"article\"><div>first</div><div>second</div><script>this should be removed</script><div>third</div></div><div>first</div><div>second</div><div>third</div>"

所以,这不起作用。我愿意:

  • 有可行的解决方案
  • 了解可行的解决方案

如果您能帮助我意识到我的问题出在哪里,我会特别感激。谢谢!

最佳答案

问题的根源是universe,它将 DOM 树扁平化为一个列表。如果再次查看输出,您将看到过滤工作正常,但树结构丢失了——因此您得到未修改的article元素(所有子元素仍在其中),后面跟着子节点减去script元素。

一个可以实现您想要的功能的 Control.Lens.Plated 组合器是 transform ,它以自下而上的方式转换“树中的每个元素”:

transform :: Plated a => (a -> a) -> a -> a

特别是,您可以使用它递归地过滤子节点:

renderWithoutScriptTag :: Text
renderWithoutScriptTag =
    let mArticle :: Maybe Taggy.Element
        mArticle =
            (fromText somehtmlSmall) ^? html .
            allAttributed (ix "id" . only "article")
        mArticleFiltered =
            fmap
                (transform (children %~ filter (\n ->
                    n ^? element . name /= Just "script")))
                mArticle
    in maybe "" (toText . Renderer.render) mArticleFiltered

关于haskell - 通过镜头过滤树中的内部元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39786824/

相关文章:

scala - 具有错误处理功能的镜头/棱镜

haskell - 如何将 Pandoc Inline 数据类型转换为 Blaze HTML 数据类型

haskell - 代数解释多态性

haskell - 复合镜头不能让绑定(bind)吗?

haskell - 镜头 : zooming newtype

haskell - 从具有默认值的镜头获取可能

haskell - 将 Haskell 函数转换为 SML

haskell - 对方法的约束取决于范围内的实例?

haskell - haskell中有类型 'Any'吗?

haskell - 使用 Control.Lens 中的多个 Getter 调用函数的干净方法是什么?