【问题标题】:Get size of a file before downloading in Python在 Python 中下载之前获取文件的大小
【发布时间】:2010-09-05 13:49:04
【问题描述】:

我正在从网络服务器下载整个目录。它工作正常,但我不知道如何在下载之前获取文件大小以比较它是否在服务器上更新。这可以像我从 FTP 服务器下载文件一样完成吗?

import urllib
import re

url = "http://www.someurl.com"

# Download the page locally
f = urllib.urlopen(url)
html = f.read()
f.close()

f = open ("temp.htm", "w")
f.write (html)
f.close()

# List only the .TXT / .ZIP files
fnames = re.findall('^.*<a href="(\w+(?:\.txt|.zip)?)".*$', html, re.MULTILINE)

for fname in fnames:
    print fname, "..."

    f = urllib.urlopen(url + "/" + fname)

    #### Here I want to check the filesize to download or not #### 
    file = f.read()
    f.close()

    f = open (fname, "w")
    f.write (file)
    f.close()

@Jon:感谢您的快速回答。它可以工作,但 Web 服务器上的文件大小略小于下载文件的文件大小。

例子:

Local Size  Server Size
 2.223.533  2.115.516
   664.603    662.121

跟CR/LF转换有关系吗?

【问题讨论】:

  • 可能。你能在上面运行 diff 看看有什么不同吗?您还看到二进制 (.zip) 文件中的文件大小差异吗?编辑:这就是像 Etags 这样的东西派上用场的地方。服务器会在发生变化时通知您,因此您无需下载完整文件即可了解。
  • 你是对的,我在打开本地文件进行写入时没有使用“wb”。奇迹般有效!谢谢

标签: python urllib


【解决方案1】:

我已经复制了你所看到的:

import urllib, os
link = "http://python.org"
print "opening url:", link
site = urllib.urlopen(link)
meta = site.info()
print "Content-Length:", meta.getheaders("Content-Length")[0]

f = open("out.txt", "r")
print "File on disk:",len(f.read())
f.close()


f = open("out.txt", "w")
f.write(site.read())
site.close()
f.close()

f = open("out.txt", "r")
print "File on disk after download:",len(f.read())
f.close()

print "os.stat().st_size returns:", os.stat("out.txt").st_size

输出这个:

opening url: http://python.org
Content-Length: 16535
File on disk: 16535
File on disk after download: 16535
os.stat().st_size returns: 16861

我在这里做错了什么? os.stat().st_size 没有返回正确的大小吗?


编辑: 好的,我发现了问题所在:

import urllib, os
link = "http://python.org"
print "opening url:", link
site = urllib.urlopen(link)
meta = site.info()
print "Content-Length:", meta.getheaders("Content-Length")[0]

f = open("out.txt", "rb")
print "File on disk:",len(f.read())
f.close()


f = open("out.txt", "wb")
f.write(site.read())
site.close()
f.close()

f = open("out.txt", "rb")
print "File on disk after download:",len(f.read())
f.close()

print "os.stat().st_size returns:", os.stat("out.txt").st_size

这个输出:

$ python test.py
opening url: http://python.org
Content-Length: 16535
File on disk: 16535
File on disk after download: 16535
os.stat().st_size returns: 16535

确保您正在打开两个文件以进行二进制读/写。

// open for binary write
open(filename, "wb")
// open for binary read
open(filename, "rb")

【讨论】:

  • 当您执行site = urllib.urlopen(link) 时,您已经执行了文件下载,因此在下载它的实际下载到您从中检索内容长度的缓冲区之前它不是大小
  • @Ciastopiekarz 我认为当您尝试 read() 文件实际上被下载到缓冲区 check this answer
  • urllib.urlopen 最迟在 3.6 中不再有效
【解决方案2】:

使用returned-urllib-object方法info(),可以得到检索到的文档的各种信息。获取当前 Google 徽标的示例:

>>> import urllib
>>> d = urllib.urlopen("http://www.google.co.uk/logos/olympics08_opening.gif")
>>> print d.info()

Content-Type: image/gif
Last-Modified: Thu, 07 Aug 2008 16:20:19 GMT  
Expires: Sun, 17 Jan 2038 19:14:07 GMT 
Cache-Control: public 
Date: Fri, 08 Aug 2008 13:40:41 GMT 
Server: gws 
Content-Length: 20172 
Connection: Close

这是一个字典,所以要获取文件的大小,你可以urllibobject.info()['Content-Length']

print f.info()['Content-Length']

并且要获取本地文件的大小(用于比较),可以使用 os.stat() 命令:

os.stat("/the/local/file.zip").st_size

【讨论】:

  • 我一直在使用这个解决方案,但是我遇到了一个边缘情况,有时没有定义内容长度标头。谁能解释为什么它不会一直被退回?
  • stackoverflow.com/questions/22087370/… 可以解释一下吗?
【解决方案3】:

基于requests 的解决方案使用 HEAD 而不是 GET(也打印 HTTP 标头):

#!/usr/bin/python
# display size of a remote file without downloading

from __future__ import print_function
import sys
import requests

# number of bytes in a megabyte
MBFACTOR = float(1 << 20)

response = requests.head(sys.argv[1], allow_redirects=True)

print("\n".join([('{:<40}: {}'.format(k, v)) for k, v in response.headers.items()]))
size = response.headers.get('content-length', 0)
print('{:<40}: {:.2f} MB'.format('FILE SIZE', int(size) / MBFACTOR))

用法

$ python filesize-remote-url.py https://httpbin.org/image/jpeg
...
Content-Length                          : 35588
FILE SIZE (MB)                          : 0.03 MB

【讨论】:

    【解决方案4】:

    文件的大小作为 Content-Length 标头发送。以下是如何使用 urllib 获取它:

    >>> site = urllib.urlopen("http://python.org")
    >>> meta = site.info()
    >>> print meta.getheaders("Content-Length")
    ['16535']
    >>>
    

    【讨论】:

      【解决方案5】:

      此外,如果您要连接的服务器支持它,请查看 Etags 以及 If-Modified-SinceIf-None-Match 标头。

      使用这些将利用网络服务器的缓存规则,如果内容未更改,将返回304 Not Modified 状态代码。

      【讨论】:

        【解决方案6】:

        对于 python3(在 3.5 上测试)方法,我建议:

        with urlopen(file_url) as in_file, open(local_file_address, 'wb') as out_file:
            print(in_file.getheader('Content-Length'))
            out_file.write(response.read())
        

        【讨论】:

          【解决方案7】:

          在 Python3 中:

          >>> import urllib.request
          >>> site = urllib.request.urlopen("http://python.org")
          >>> print("FileSize: ", site.length)
          

          【讨论】:

          • 这会下载文件!
          【解决方案8】:

          @PabloG 关于本地/服务器文件大小差异

          以下是其可能发生原因的高级说明性说明:

          磁盘上的大小有时与数据的实际大小不同。 它取决于底层文件系统及其对数据的操作方式。 正如您在格式化闪存驱动器时可能在 Windows 中看到的那样,系统会要求您提供“块/集群大小”,它会有所不同 [512b - 8kb]。 当一个文件被写入磁盘时,它被存储在磁盘块的“某种链表”中。 当某个块用于存储文件的一部分时,不会有其他文件内容存储在同一个块中,因此即使该块没有占用整个块空间,该块也会被其他文件渲染为不可用。

          示例: 当文件系统被划分为 512b 块时,我们需要存储 600b 的文件,将占用两个块。第一个块将被充分利用,而第二个块将仅使用 88b,剩余的 (512-88)b 将无法使用,导致“file-size-on-disk”为 1024b。 这就是为什么 Windows 对“文件大小”和“磁盘大小”有不同表示法的原因。

          注意: 更小/更大的 FS 块有不同的优缺点,因此在使用您的文件系统之前请进行更好的研究。

          【讨论】:

            【解决方案9】:

            对于使用 Python 3 并使用 requests 包寻找快速解决方案的任何人:

            import requests 
            response = requests.head( 
                "https://website.com/yourfile.mp4",  # Example file 
                allow_redirects=True
            )
            print(response.headers['Content-Length']) 
            

            注意:并非所有响应都有Content-Length,因此您的应用程序需要检查它是否存在。

            if 'Content-Length' in response.headers:
                ... # Do your stuff here 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-03-14
              • 1970-01-01
              • 2013-06-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多