如您所见,simpleHttp 函数返回一个惰性字节串。在 TagSoup 中有几种方法可以解决这个问题。
首先,事实证明你可以直接解析它。函数parseTags 有签名:
parseTags :: StringLike str => str -> [Tag str]
这意味着它可以使用StringLike 实例解析任何类型的str,如果您查看Text.StringLike 模块文档,您会发现懒惰的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 请求的响应标头和/或检查 HTML 中的 <meta> 标记以确定真正的编码。但是,如果我们只是假设编码是 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>")
使用utf8-string 包中的Data.ByteString.Lazy.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>")