Каков наилучший способ получить данные из URL и проанализировать их на Haskell? - PullRequest
0 голосов
/ 06 мая 2019

У меня проблемы с разбором данных из URL.

У меня есть URL с "https://", поэтому я думаю, что я должен использовать импорт Network.HTTP.Conduit Но

simpleHttp url

возвращает L.ByteString Я действительно не понимаю, что я должен делатьпосле этого

Итак, у меня есть такой код для получения данных

toStrict1 :: L.ByteString -> B.ByteString
toStrict1 = B.concat . L.toChunks

main :: IO ()
main = do
    lbs <- simpleHttp url
    let page = toStrict1 lbs

и пример разбора

    let lastModifiedDateTime = fromFooter $ parseTags doc
    putStrLn $ "wiki.haskell.org was last modified on " ++ lastModifiedDateTime
    where fromFooter = unwords . drop 6 . words . innerText . take 2 . dropWhile (~/= "<li id=footer-info-lastmod>")

Как я могу объединить эти две части кода?

1 Ответ

0 голосов
/ 11 мая 2019

Как вы видели, функция simpleHttp возвращает ленивую строку байтов. Есть несколько способов справиться с этим в TagSoup.

Во-первых, получается, что вы можете разобрать его напрямую. Функция parseTags имеет подпись:

parseTags :: StringLike str => str -> [Tag str]

означает, что он может анализировать любой тип str с экземпляром StringLike, и если вы посмотрите документацию модуля Text.StringLike, вы увидите, что у lazy ByteStrings есть экземпляр StringLike.

Однако, если вы идете по этому пути, вы должны знать, что все в некотором роде «поймано в ловушку» в мире ByteString, поэтому вы должны писать свой код, используя версии функций, такие как words и unwords, которые совместимы со строками, и даже вашему putStrLn нужен адаптер. Полный рабочий пример будет выглядеть так:

import Network.HTTP.Conduit
import Text.HTML.TagSoup
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as CL

main :: IO ()
main = do
    lbs <- simpleHttp "https://wiki.haskell.org"
    let lastModifiedDateTime = fromFooter $ parseTags lbs
    putStrLn $ "wiki.haskell.org was last modified on " 
        ++ CL.unpack lastModifiedDateTime
    where fromFooter = CL.unwords . drop 6 . CL.words
              . innerText . take 2 . dropWhile (~/= "<li id=footer-info-lastmod>")

и работает нормально:

> main
wiki.haskell.org was last modified on 9 September 2013, at 22:38.
>

Функции из Data.ByteString.Lazy.Char8 в основном предполагают, что строка байтов кодируется в ASCII, что достаточно близко для работы этого примера.

Однако было бы более надежно декодировать строку байтов на основе правильной кодировки символов в допустимый тип текста. Два основных типа текста в Haskell - это тип по умолчанию String, который является неэффективным и медленным, но с ним легко работать, и тип Text, который очень эффективен, но немного сложнее. (Как и ByteString, вам нужно использовать Text -совместимые версии функций, таких как words и т. Д.) Оба String и Text имеют StringLike экземпляров, поэтому они оба отлично работают с TagSoup.

Если бы мы собирались написать код производственного качества, мы бы на самом деле обращались к заголовкам ответа из HTTP-запроса и / или проверяли тег <meta> в HTML, чтобы определить реальную кодировку. Но, если мы просто предположим, что кодировка UTF-8 (это так), версия Text выглядит следующим образом:

import Network.HTTP.Conduit
import Text.HTML.TagSoup
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.ByteString.Lazy as BL

main :: IO ()
main = do
    lbs <- simpleHttp "https://wiki.haskell.org"
    let lastModifiedDateTime = fromFooter $ parseTags (TL.decodeUtf8 lbs)
    putStrLn $ "wiki.haskell.org was last modified on " 
        ++ TL.unpack lastModifiedDateTime
    where fromFooter = TL.unwords . drop 6 . TL.words
              . innerText . take 2 . dropWhile (~/= "<li id=footer-info-lastmod>")

и String версия с использованием Data.ByteString.Lazy.UTF8 из пакета utf8-string выглядит следующим образом:

import Network.HTTP.Conduit
import Text.HTML.TagSoup
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.UTF8 as BL

main :: IO ()
main = do
    lbs <- simpleHttp "https://wiki.haskell.org"
    let lastModifiedDateTime = fromFooter $ parseTags (BL.toString lbs)
    putStrLn $ "wiki.haskell.org was last modified on " 
        ++ lastModifiedDateTime
    where fromFooter = unwords . drop 6 . words
              . innerText . take 2 . dropWhile (~/= "<li id=footer-info-lastmod>")
...