Как вы видели, функция 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>")