首页 > 解决方案 > 如何使用 Haskell 从 Internet 下载文件?

问题描述

我只是在尝试做类似的事情wget,我从 Internet 下载文件。我看到曾经有一个名为http-wget的包,但它已被弃用,取而代之的是 http-conduit。

Http-conduit 有一个简单的例子来说明如何使用httpBS. 因此,在此之后,我得到了这个工作:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = do
  let url = "https://www.example.com/sitemap.xml"
  resp <- httpBS url
  B8.putStrLn $ getResponseBody resp

这适用于从 URL 获取文件名(sitemap.xml):

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = do
  let url = "https://www.example.com/sitemap.xml"
  let urlParts = B8.split '/' $ B8.pack url
  let fileName = Prelude.last urlParts
  B8.putStrLn fileName

但我不能把它们放在一起:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = do
  let url = "https://www.example.com/sitemap.xml"
  let urlParts = B8.split '/' $ B8.pack url
  let fileName = Prelude.last urlParts
  resp <- httpBS url
  B8.putStrLn $ getResponseBody resp

这给出了错误:

ny1920-parse.hs:12:41: error:
    • Couldn't match type ‘Request’ with ‘[Char]’
      Expected type: String
        Actual type: Request
    • In the first argument of ‘B8.pack’, namely ‘url’
      In the second argument of ‘($)’, namely ‘B8.pack url’
      In the expression: B8.split '/' $ B8.pack url
   |
12 |   let urlParts = B8.split '/' $ B8.pack url
   |                                         ^^^

所以我只需要转换String -> Request?在 http-conduit 中显然有一个功能,但它没有按预期工作 - 我仍然得到同样的错误。

我可以强制 URL 成为这样的请求:

  let url = "https://www.example.com/sitemap.xml" :: Request

但是当然这会破坏我分解文件名的部分,因为它需要 a[Char]而不是 a Request

所以我被困住了——如果我将 URL 设为字符串,它会破坏 http-conduit。如果我把它作为一个请求,它会破坏字符串操作。

我觉得这么简单的事情不应该这么难,不是吗?

编辑:好的,所以我几乎可以让它与这个添加一起工作:

  let urlParts = B8.split '/' $ B8.pack (show url)

编译,但它使文件名损坏。试图打印出文件名给出:"1.1\n}\n"而不是sitemap.xml.

标签: haskell

解决方案


我不同意这里的另一个答案:分裂/自己是个坏主意。不要尝试实现临时 URL 解析器;这比你想象的要难。相反,重新使用您已经拥有的解析:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Client
import Network.HTTP.Simple
import Network.URI
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = do
    let request = "https://www.example.com/sitemap.xml"
        fileName = Prelude.last . pathSegments . getUri $ request
    resp <- httpBS request
    B8.putStrLn $ getResponseBody resp

有关可以从 URI 中提取的部分的更多信息,请参阅文档。


推荐阅读