16

株式市場の相場 (サンプル データ)を含む XML ファイルを定期的に提供する Web ページからデータを取得しようとしています。XML の構造は非常に単純で、次のようなものです。

<?xml version="1.0"?>
<Contents>
  <StockQuote Symbol="PETR3" Date="21-12-2010" Time="13:20" Price="23.02" />
</Contents>

(それ以上ですが、例としてはこれで十分です)。

私はそれをデータ構造に解析したいと思います:

 data Quote = Quote { symbol :: String, 
                      date   :: Data.Time.Calendar.Day, 
                      time   :: Data.Time.LocalTime.TimeOfDay,
                      price  :: Float}

私は Parsec がどのように機能するか (Real World Haskell book のレベルで) を多かれ少なかれ理解しており、Text.XMLライブラリを少し試してみましたが、開発できたのは機能するコードだけでしたが、そのような単純なタスクには大きすぎて、中途半端なハックであり、できる限りのことではありません。

私はパーサーと XML についてあまり知りません (基本的に RWH の本で読んだことは知っていますが、パーサーを使用したことはありません) (私は統計プログラミングと数値プログラミングを行うだけで、コンピューター科学者ではありません)。各要素を手作業で解析したり、純粋な文字列を解析したりすることなく、モデルが何であるかを判断して情報をすぐに抽出できる XML 解析ライブラリはありますか?

私は次のようなことを考えています:

  myParser = do cont  <- openXMLElem "Contents"
                quote <- openXMLElem "StockQuote" 
                symb <- getXMLElemField "Symbol"
                date <- getXMLElemField "Date"
                (...) 
                closequote <- closeXMLElem "StockQuote"
                closecont  <- closeXMLElem "Contents"
                return (symb, date)


  results = parse myParser "" myXMLString

ここでは、純粋な文字列を処理してコンビネータを自分で作成する必要はありません (私はそれが嫌いです)。

編集: おそらく、一般的なパーサー (Parsec だけでなく) と XML に関する最低限のことについて、少し (これを正しく行うのに十分なだけ) 読む必要があります。皆さん、何かお勧めはありますか?

解析する必要がある実際の文字列は次のとおりです。

 stringTest = "<?xml version=\"1.0\"?>\r\n<ComportamentoPapeis><Papel Codigo=\"PETR3\" 
 Nome=\"PETROBRAS ON\" Ibovespa=\"#\" Data=\"05/01/201100:00:00\" 
 Abertura=\"29,80\" Minimo=\"30,31\" Maximo=\"30,67\" Medio=\"30,36\" 
 Ultimo=\"30,45\" Oscilacao=\"1,89\" Minino=\"29,71\"/></ComportamentoPapeis>\r\n"

EDIT2:

私は次のことを試しました(readFloat、readQuoteTimeなどは、文字列から物を読み取るための単なる関数です)。

bvspaParser :: (ArrowXml a) => a XmlTree Quote
bvspaParser = hasName "ComportamentoPapeis" /> hasName "Papel" >>> proc x -> do
   (hour,date) <- readQuoteTime ^<< getAttrValue "Data" -< x
   quoteCode   <- getAttrValue "Codigo" -< x
   openPrice   <- readFloat ^<< getAttrValue "Abertura" -< x
   minim       <- readFloat ^<< getAttrValue "Minimo" -< x
   maxim       <- readFloat ^<< getAttrValue "Maximo" -< x
   ultimo      <- readFloat ^<< getAttrValue "Ultimo" -< x
   returnA     -< Quote quoteCode (LocalTime date hour) openPrice minim maxim ultimo

docParser :: String -> IO [Quote]
docParser  str = runX $ readString [] str >>> (parseXmlDocument False) >>> bvspaParser

ghciで呼び出すと:

*Main> docParser stringTest >>= print
[]

何か問題がありますか?

4

5 に答える 5

20

Haskell 用に作成された XML ライブラリは、解析を代行してくれるものがたくさんあります。xml というライブラリをお勧めします ( http://hackage.haskell.org/package/xmlを参照)。それを使用すると、次のように簡単に書くことができます。

let contents = parseXML source
    quotes   = concatMap (findElements $ simpleName "StockQuote") (onlyElems contents)
    symbols  = map (findAttr $ simpleName "Symbol") quotes
    simpleName s = QName s Nothing Nothing
print symbols

このスニペットは[Just "PETR3"]サンプル XML の結果として出力され、必要なすべてのデータを収集するために簡単に拡張できます。記述したスタイルでプログラムを書くには、Maybe モナドを使用する必要があります。xml 検索関数は、タグ、要素、または属性が見つかったかどうかを通知する Maybe String を返すことが多いためです。関連する質問も参照してください:どの Haskell XML ライブラリを使用しますか?

于 2011-01-06T20:42:28.510 に答える
5

次のスニペットでは、xml-enumerator を使用しています。日付と時刻をテキストとして残します (これらの解析は、読者の課題として残されています)。

{-# LANGUAGE OverloadedStrings #-}
import Text.XML.Enumerator.Parse
import Data.Text.Lazy (Text, unpack)

data Quote = Quote { symbol :: Text
                   , date   :: Text
                   , time   :: Text
                   , price  :: Float}
  deriving Show

main = parseFile_ "test.xml" (const Nothing) $ parseContents

parseContents = force "Missing Contents" $ tag'' "Contents" parseStockQuote
parseStockQuote = force "Missing StockQuote" $ flip (tag' "StockQuote") return $ do
    s <- requireAttr "Symbol"
    d <- requireAttr "Date"
    t <- requireAttr "Time"
    p <- requireAttr "Price"
    return $ Quote s d t (read $ unpack p)
于 2011-01-06T21:01:23.170 に答える
5

単純な xml 解析の場合、tagsoup を使えば間違いありません。http://hackage.haskell.org/package/tagsoup

于 2011-01-06T20:26:45.057 に答える
4

私は過去にHaskellXMLToolboxを使用しました。の線に沿った何か

{-# LANGUAGE Arrows #-}

quoteParser :: (ArrowXml a) => a XmlTree Quote
quoteParser =
    hasName "Contents" /> hasName "StockQuote" >>> proc x -> do
    symbol <- getAttrValue "Symbol" -< x
    date <- readTime defaultTimeLocale "%d-%m-%Y" ^<< getAttrValue "Date" -< x
    time <- readTime defaultTimeLocale "%H:%M" ^<< getAttrValue "Time" -< x
    price <- read ^<< getAttrValue "Price" -< x
    returnA -< Quote symbol date time price

parseQuoteDocument :: String -> IO (Maybe Quote)
parseQuoteDocument xml =
    liftM listToMaybe . runX . single $
    readString [] xml >>> getChildren >>> quoteParser
于 2011-01-06T21:42:30.953 に答える
4

このライブラリを使用する方法は他にもありますが、このような単純なもののために、sax パーサーをまとめました。

import Prelude as P
import Text.XML.Expat.SAX
import Data.ByteString.Lazy as L

parsexml txt = parse defaultParseOptions txt :: [SAXEvent String String]

main = do
  xml <- L.readFile "stockinfo.xml"
  return  $ P.filter stockquoteelement (parsexml xml)

  where
    stockquoteelement (StartElement "StockQuote" attrs) = True
    stockquoteelement _ = False

そこから、どこに行くべきかを知ることができます。Text.XML.Expat.Annotated を使用して、上記で探しているものに似た構造に解析することもできます。

parsexml txt = parse defaultParseOptions txt :: (LNode String String, Maybe XMLParseError)

次に、Text.XML.Expat.Procを使用して構造をサーフします。

于 2011-01-06T20:44:56.427 に答える