Haskell HXT для извлечения списка значений

Я пытаюсь разобраться в HXT с XPath и стрелками одновременно, и я полностью застрял в том, как решить эту проблему. У меня есть следующий HTML:

<div>
<div class="c1">a</div> 
<div class="c2">b</div> 
<div class="c3">123</div> 
<div class="c4">234</div> 
</div>

который я извлек в HXT XmlTree. Что я хотел бы сделать, это определить функцию (я думаю?):

getValues :: [String] -> IOSArrow Xmltree [(String, String)]

Который, если используется как getValues ["c1", "c2", "c3", "c4"], достанешь мне:

[("c1", "a"), ("c2", "b"), ("c3", "123"), ("c4", "234")]

Помогите, пожалуйста?

2 ответа

Вот один из подходов (мои типы немного более общие, и я не использую XPath):

{-# LANGUAGE Arrows #-}
module Main where

import qualified Data.Map as M
import Text.XML.HXT.Arrow

classes :: (ArrowXml a) => a XmlTree (M.Map String String)
classes = listA (divs >>> divs >>> pairs) >>> arr M.fromList
  where
    divs = getChildren >>> hasName "div"
    pairs = proc div -> do
      cls <- getAttrValue "class" -< div
      val <- deep getText         -< div
      returnA -< (cls, val)

getValues :: (ArrowXml a) => [String] -> a XmlTree [(String, Maybe String)]
getValues cs = classes >>> arr (zip cs . lookupValues cs)
  where lookupValues cs m = map (flip M.lookup m) cs

main = do
  let xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\
            \<div class='c3'>123</div><div class='c4'>234</div></div>"

  print =<< runX (readString [] xml >>> getValues ["c1", "c2", "c3", "c4"])

Я, вероятно, запустил бы стрелку, чтобы получить карту, а затем выполнить поиск, но этот способ также работает.


Чтобы ответить на ваш вопрос о listA: divs >>> divs >>> pairs это стрелка списка с типом a XmlTree (String, String)То есть это недетерминированные вычисления, которые принимают дерево XML и возвращают пары строк.

arr M.fromList имеет тип a [(String, String)] (M.Map String String), Это означает, что мы не можем просто написать divs >>> divs >>> pairs, поскольку типы не совпадают.

listA решает эту проблему: она рушится divs >>> divs >>> pairs в детерминистическую версию с типом a XmlTree [(String, String)], что именно то, что нам нужно.

Вот способ сделать это с помощью BeautifulSoup:

-- For the join function.
import Data.String.Utils
import Text.HandsomeSoup
import Text.XML.HXT.Core

-- Of each element, get class attribute and text.
getItem = (this ! "class" &&& (this /> getText))  
getItems selectors = css (join "," selectors) >>> getItem

main = do
  let selectors = [".c1", ".c2", ".c3", ".c4"]
  items <- runX (readDocument [] "data.html" >>> getItems selectors)
  print items

data.html - это файл HTML.

Несмотря на то, что прошло 10 лет после первоначальной публикации, это все еще очень полезный совет.

Для всех, кто застрял в обработке XML в Haskell в 2020 году, могу подтвердить, что первый пример отлично работает в следующей системе:

ghci --version Система компиляции Glorious Glasgow Haskell, версия 8.8.4

Mac OS Catalina (10.15.7)

Большое спасибо за помощь - это сэкономило мне много времени.

Другие вопросы по тегам