我应该从“urllib.request.urlretrieve(..)”切换到“urllib.request.urlopen(..)”吗?

3

1. 废弃问题

Python 3.7中,我使用urllib.request.urlretrieve(..)函数从URL下载一个大文件。在文档(https://docs.python.org/3/library/urllib.request.html)中,在urllib.request.urlretrieve(..)文档的上方,我读到以下内容:

遗留接口
以下函数和类从Python 2模块urllib(而不是urllib2)移植而来。它们可能在未来某个时候被废弃。

 

2. 寻找替代方案

为了使我的代码具有未来性,我正在寻找替代方案。官方Python文档没有提到特定的替代方案,但看起来urllib.request.urlopen(..)是最直接的选择。它在文档页面的顶部。

不幸的是,其他替代方案 - 如urlopen(..) - 没有提供reporthook参数。这个参数是您传递给urlretrieve(..)函数的可调用对象。然后,urlretrieve(..)定期使用以下参数调用它:

  • 块编号。
  • 块大小
  • 总文件大小

我用它来更新进度条。这就是为什么我在替代方案中缺少reporthook参数的原因。

 

3. urlretrieve(..) vs urlopen(..)

我发现urlretrieve(..)只是使用urlopen(..)。请参见Python 3.7安装中的request.py代码文件(Python37/Lib/urllib/request.py):

_url_tempfiles = []
def urlretrieve(url, filename=None, reporthook=None, data=None):
    """
    Retrieve a URL into a temporary location on disk.

    Requires a URL argument. If a filename is passed, it is used as
    the temporary file location. The reporthook argument should be
    a callable that accepts a block number, a read size, and the
    total file size of the URL target. The data argument should be
    valid URL encoded data.

    If a filename is passed and the URL points to a local resource,
    the result is a copy from local file to new file.

    Returns a tuple containing the path to the newly created
    data file as well as the resulting HTTPMessage object.
    """
    url_type, path = splittype(url)

    with contextlib.closing(urlopen(url, data)) as fp:
        headers = fp.info()

        # Just return the local path and the "headers" for file://
        # URLs. No sense in performing a copy unless requested.
        if url_type == "file" and not filename:
            return os.path.normpath(path), headers

        # Handle temporary file setup.
        if filename:
            tfp = open(filename, 'wb')
        else:
            tfp = tempfile.NamedTemporaryFile(delete=False)
            filename = tfp.name
            _url_tempfiles.append(filename)

        with tfp:
            result = filename, headers
            bs = 1024*8
            size = -1
            read = 0
            blocknum = 0
            if "content-length" in headers:
                size = int(headers["Content-Length"])

            if reporthook:
                reporthook(blocknum, bs, size)

            while True:
                block = fp.read(bs)
                if not block:
                    break
                read += len(block)
                tfp.write(block)
                blocknum += 1
                if reporthook:
                    reporthook(blocknum, bs, size)

    if size >= 0 and read < size:
        raise ContentTooShortError(
            "retrieval incomplete: got only %i out of %i bytes"
            % (read, size), result)

    return result

4. 结论

综合以上内容,我看到三种可能的决定:

  1. 我保持我的代码不变。希望urlretrieve(..)函数不会被弃用。

  2. 我编写一个替代函数,在外部表现像urlretrieve(..)并在内部使用urlopen(..)。实际上,这样的函数将是上面代码的复制粘贴。与使用官方的urlretrieve(..)相比,这种做法感觉不太干净。

  3. 我编写一个替代函数,在外部表现像urlretrieve(..),但在内部完全使用不同的东西。但是嘿,为什么要这样做呢?urlopen(..)没有被弃用,为什么不使用它呢?

你会选择哪个决定?


如果你很担心,那么就将 urlretrieve() 复制到你的代码中,并在需要时立即使用它,或者只有在没有原始的 urllib.request.urlretrieve() 时才使用它。 - furas
1
请查看类似问题的答案:alternative-of-urllib-urlretrieve-in-python-3-5how-to-download-a-file-over-http - Paul Rougieux
1个回答

3
以下示例使用 urllib.request.urlopen 下载从联合国粮农组织统计数据库获取的大洋洲作物生产数据所在的 zip 文件。该示例中,有必要定义一个最小标头,否则 FAOSTAT 将抛出 Error 403: Forbidden
import shutil
import urllib.request
import tempfile

# Create a request object with URL and headers    
url = “http://fenixservices.fao.org/faostat/static/bulkdownloads/Production_Crops_Livestock_E_Oceania.zip”
header = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) '}
req = urllib.request.Request(url=url, headers=header)

# Define the destination file
dest_file = tempfile.gettempdir() + '/' + 'crop.zip'
print(f“File located at:{dest_file}”)

# Create an http response object
with urllib.request.urlopen(req) as response:
    # Create a file object
    with open(dest_file, "wb") as f:
        # Copy the binary content of the response to the file
        shutil.copyfileobj(response, f)

基于https://dev59.com/yXVD5IYBdhLWcg3wTZxm#48691447的请求部分和https://dev59.com/m2Yr5IYBdhLWcg3w6eI3#66591873的头部分,还可以参考urllib的文档:https://docs.python.org/3/howto/urllib2.html


谢谢!这太棒了 :-) - K.Mulier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接