使用Haskell中的Arrows和HXT从Html表中的s记录

时间:2016-12-19 03:18:39

标签: html haskell arrows hxt

希望使用HXT从格式良好的HTMl表中的表中提取记录。我已经回顾了关于SO和HXT文档的几个例子,例如:

我的问题是:

  

我想通过已知的id唯一地标识一个表,然后为每个表标识一个表   在该表中的tr,创建一个记录对象并将其作为列表返回   记录。

这是我的HTML

<!DOCTYPE html>
<head>
  <title>FakeHTML</title>
</head>
<body>
  <table id="fakeout-dont-get-me">
    <thead><tr><td>Null</td></tr></thead>
    <tbody><tr><td>Junk!</td></tr></tbody>
  </table>
  <table id="Greatest-Table">
    <thead>
      <tr><td>Name</td><td>Favorite Rock</td></tr>
    </thead>
    <tbody>
      <tr id="rock1">
        <td>Fred</td>
        <td>Igneous</td>
      </tr>
      <tr id="rock2">
        <td>Bill</td>
        <td>Sedimentary</td>
      </tr>
    </tbody>
  </table>
</body>
</html>

这是我尝试的代码,以及解析此问题的两种不同方法。首先,进口......

{-# LANGUAGE Arrows, OverloadedStrings, DeriveDataTypeable, FlexibleContexts  #-}
import Text.XML.HXT.Core
import Text.HandsomeSoup
import Text.XML.HXT.XPath.XPathEval
import Data.Tree.NTree.TypeDefs
import Text.XML.HXT.XPath.Arrows

我想要的是Rockrecs列表,例如来自......

recs = [("rock1", "Name", "Fred", "Favorite Rock", "Igneous"),
        ("rock2", "Name", "Bill", "Favorite Rock", "Sedimentary")]

data Rockrec = Rockrec { rockID:: String,
                         rockName :: String,
                         rockFav :: String} deriving Show

rocks = [(\(a,_,b,_,c) -> Rockrec a b c ) r | r <- recs]
-- [Rockrec {rockID = "rock1", rockName = "Fred", rockFav = "Igneous"},
--  Rockrec {rockID = "rock2", rockName = "Bill", rockFav = "Sedimentary"}]

这是我的第一种方式,它在返回一堆[XMLTree]后在runLA上使用了绑定。也就是说,我做第一次解析只是为了得到正确的表,然后我在第一次抓取之后处理树行。

尝试1

getTab = do
  dt <- Prelude.readFile "fake.html"
  let html = parseHtml dt
  tab <- runX $ html //> hasAttrValue "id" (== "Greatest-Table")
  return tab
  -- hmm, now this gets tricky...

-- table <- getTab

node tag = multi (hasName tag)

-- a la https://stackoverflow.com/questions/3901492/running-haskell-hxt-outside-of-io?rq=1
getIt  :: ArrowXml cat => cat (Data.Tree.NTree.TypeDefs.NTree XNode) (String, String)
getIt = (node "tr" >>>
         (getAttrValue "id" &&& (node "td" //> getText)))

这种方式有效。我需要按摩一下,但可以让它运行...

-- table >>= runLA getIt
-- [("","Name"),("","Favorite Rock"),("rock1","Fred"),("rock1","Igneous"),("rock2","Bill"),("rock2","Sedimentary")]

这是第二种方法,受https://wiki.haskell.org/HXT/Practical/Simple1的启发。在这里,我认为我依赖于{ - #LANGUAGE Arrows - }中的某些东西(巧合地突破了我对上面的rec的列表理解),使用proc函数在更易读的do块中执行此操作。也就是说,我甚至无法编译这个的最小版本:

尝试2

 getR :: ArrowXml cat => cat XmlTree Rockrec
 getR = (hasAttrValue "id" (== "Greatest-Table")) >>>
   proc x -> do
     rockId <- getText -< x
     rockName <- getText -< x
     rockFav <- getText -< x
     returnA -< Rockrec rockId rockName rockFav

修改

对于类型的问题,以回应以下来自Alec的评论

λ> getR [table]

<interactive>:56:1-12: error:
    • Couldn't match type ‘NTree XNode’ with ‘[[XmlTree]]’
      Expected type: [[XmlTree]] -> Rockrec
        Actual type: XmlTree -> Rockrec
    • The function ‘getR’ is applied to one argument,
      its type is ‘cat0 XmlTree Rockrec’,
      it is specialized to ‘XmlTree -> Rockrec’
      In the expression: getR [table]
      In an equation for ‘it’: it = getR [table]
λ> getR table

<interactive>:57:1-10: error:
    • Couldn't match type ‘NTree XNode’ with ‘[XmlTree]’
      Expected type: [XmlTree] -> Rockrec
        Actual type: XmlTree -> Rockrec
    • The function ‘getR’ is applied to one argument,
      its type is ‘cat0 XmlTree Rockrec’,
      it is specialized to ‘XmlTree -> Rockrec’
      In the expression: getR table
      In an equation for ‘it’: it = getR table

结束编辑

即使我没有选择元素,我也无法运行上述元素。我还有点不知道我应该做些什么,比如将第一个td放在rockName中,将第二个td放在rockFav中,如何在这些上包含一个迭代器(假设我有很多td字段,而不是只有2个。)

关于如何更轻松地进行此操作的任何进一步的一般提示。

1 个答案:

答案 0 :(得分:1)

HXT/Practical/Google1我认为我能够拼凑出一个解决方案。

{-# LANGUAGE Arrows #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hanzo where
import Text.HandsomeSoup
import Text.XML.HXT.Cor

atTag tag =
  deep (isElem >>> hasName tag)
text =
  deep isText >>> getText

data Rock = Rock String String String deriving Show    
rocks =
  atTag "tbody" //> atTag "tr"
  >>> proc x -> do
        rowID <- x >- getAttrValue "id"
        name <- x >- atTag "td" >. (!! 0) >>> text
        kind <- x >- atTag "td" >. (!! 1) >>> text
        returnA -< Rock rowID name kind

main = do
  dt <- readFile "html.html"
  result <- runX $ parseHtml dt
                   //> hasAttrValue "id" (== "Greatest-Table")
                   >>> rocks
  print result

关键的道路是这些:

  • 您的箭头适用于元素的溪流,但不适用于单个元素。这是ArrowList约束。因此,调用getText三次会产生令人惊讶的行为,因为getText表示您在通过<table>传播proc x -> do {...}元素的过程中可能获得的所有不同文本值。< / p>

  • 我们可以做的是关注我们想要的流:<tr>内的<tbody>流。对于每个表行,我们获取ID属性值和前两个<td>的文本。

  • 这似乎不是最优雅的解决方案,但我们可以将其编入索引的一种方法是使用(>.) :: ArrowList cat => cat a b -> ([b] -> c) -> cat a c组合器对其进行过滤。

  • 最后一个技巧,我在实际维基示例中注意到的一个:我们可以使用deepisElem/isText来专注于我们想要的节点。 XML树很吵!