HXT获得第一个元素:重构奇怪的箭头

时间:2014-04-26 12:18:38

标签: haskell hxt

我需要获取<p>的第一个<div class="about">的文本内容,编写以下代码:

tagTextS :: IOSArrow XmlTree String
tagTextS = getChildren >>> getText >>> arr stripString

parseDescription :: IOSArrow XmlTree String
parseDescription =
  (
   deep (isElem >>> hasName "div" >>> hasAttrValue "id" (== "company_about_full_description"))
   >>> (arr (\x -> x) /> isElem  >>> hasName "p") >. (!! 0) >>> tagTextS
  ) `orElse` (constA "")

看看这个arr (\x -> x) - 没有它我无法达到结果。

  • 有没有更好的方式来撰写parseDescription
  • 另一个问题 为什么我需要在arr之前和hasName "p"之后使用括号? (一世 实际上找到了这个解决方案here

2 个答案:

答案 0 :(得分:4)

XPath

可能是这样的
import "hxt-xpath" Text.XML.HXT.XPath.Arrows (getXPathTrees)

...

xp = "//div[@class='about']/p[1]"

parseDescription = getXPathTrees xp >>> getChildren >>> getText

答案 1 :(得分:2)

另一个使用hxt核心的建议。

要强制执行第一个孩子,不能通过 getChildren 输出来完成,因为hxt箭头有一个特定的(&gt;&gt;&gt;),它将后续箭头映射到先前输出的每个列表项而不是输出列表,如haskellWiki hxt page中所述,虽然这是一个旧的定义,实际上它来自Category (.) composition

getNthChild 可以从Control.Arrow.ArrowTree的getChildren攻击

import Data.Tree.Class (Tree)
import qualified Data.Tree.Class as T

-- if the nth element does not exist it will return an empty children list

getNthChild :: (ArrowList a, Tree t) => Int -> a (t b) (t b)
getNthChild n = arrL (take 1 . drop n . T.getChildren)

那么你的parseDescription就可以采用这种形式:

-- importing Text.XML.HXT.Arrow.XmlArrow (hasName, hasAttrValue)

parseDescription = 
    deep (isElem >>> hasName "div" >>> hasAttrValue "class" (== "about") 
          >>> getNthChild 0 >>> hasName "p"
          ) 
    >>> getChildren >>> getText

<强>更新即可。我找到了另一种使用changeChildren的方式:

getNthChild :: (ArrowTree a, Tree t) => Int -> a (t b) (t b)
getNthChild n = changeChildren (take 1 . drop n) >>> getChildren

更新:避免元素间距 - 节点过滤非元素子元素

import qualified Text.XML.HXT.DOM.XmlNode as XN

getNthChild :: (ArrowTree a, Tree t, XN.XmlNode b) => Int -> a (t b) (t b)
getNthChild n = changeChildren (take 1 . drop n . filter XN.isElem) >>> getChildren