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)
Большое спасибо за помощь - это сэкономило мне много времени.