15

如何使用 python 下载带有进度报告但不提供文件名的文件。

我试过 urllib.urlretrieve 但我似乎必须为下载的文件提供一个文件名才能另存为。

例如:

我不想提供这个:

urllib.urlretrieve("http://www.mozilla.com/products/download.html?product=firefox-3.6.3&os=win&lang=en-US", "/tmp/firefox.exe")

只是这个:

urllib.urlretrieve("http://www.mozilla.com/products/download.html?product=firefox-3.6.3&os=win&lang=en-US", "/tmp/")

但如果我这样做,我会收到此错误:

IOError: [Errno 21] Is a directory: '/tmp'

也无法从某些 URL 示例中获取文件名:

http://www.mozilla.com/products/download.html?product=firefox-3.6.3&os=win&lang=en-US

4

8 回答 8

28

这是使用 python3 并且在 url 中未指定文件名的完整方法:

from urllib.request import urlopen
from urllib.request import urlretrieve
import cgi

url = "http://cloud.ine.ru/s/JDbPr6W4QXnXKgo/download"
remotefile = urlopen(url)
blah = remotefile.info()['Content-Disposition']
value, params = cgi.parse_header(blah)
filename = params["filename"]
urlretrieve(url, filename)

结果你应该得到cargo_live_animals_parrot.jpg文件

于 2018-04-09T12:58:43.113 回答
11

澄清问题后编辑...

urlparse.urlsplit将获取您正在打开的 url 并将其拆分为其组成部分,然后您可以获取该path部分并使用 last /-delimited 块作为文件名。

import urllib, urlparse

split = urlparse.urlsplit(url)
filename = "/tmp/" + split.path.split("/")[-1]
urllib.urlretrieve(url, filename)
于 2010-05-08T19:52:56.037 回答
2

您指定的 URL 根本不引用文件。这是一个重定向到运行一些 javascript 的网页,这会导致您的网络浏览器下载文件。我的浏览器从相关 URL 定向到(镜像)的实际地址是:

http://mozilla.mirrors.evolva.ro//firefox/releases/3.6.3/win32/en-US/Firefox%20Setup%203.6.3.exe

我相信网络服务器有两种方式指定下载文件的名称;

  1. URL 路径的最后一段
  2. Content-Disposition头,可以指定要使用的其他文件名

对于您要下载的文件,我认为您只需要 URL 的最后一个路径段(但使用文件的实际 URL,而不是选择要使用的镜像文件的网页)。但是对于某些下载,您需要从Content-Disposition标题中获取要使用的文件名。

于 2010-05-08T20:47:43.837 回答
2

urlopen, 它创建了一个类似文件的对象,可用于读取数据而无需将其保存到本地文件:

from urllib2 import urlopen

f = urlopen("http://example.com/")
for line in f:
  print len(line)
f.close()

(我不确定这是否是您所要求的。)

于 2010-05-08T19:44:06.737 回答
1

我结束了

os.system('wget -P /tmp http://www.mozilla.com/products/download.html?'
          'product=firefox-3.6.3&os=win&lang=en-US')
于 2016-05-15T20:32:37.107 回答
0

urlgrabber.urlgrab() will use the basename of the URL passed to it as the filename. Note that it will ignore the Content-Disposition header.

于 2010-05-08T21:18:34.393 回答
0

快速浏览一下 Firefox 页面上的 javascript 会发现:

// 2. Build download.mozilla.org URL out of those vars.
download_url = "http://download.mozilla.org/?product=";
download_url += product + '&os=' + os + '&lang=' + lang;

因此,只需将您的网址更改为:

http://www.mozilla.com/products/download.html?product=firefox-3.6.3&os=win&lang=en-US

http://download.mozilla.org/?product=firefox-3.6.3&os=win&lang=en-US

所以现在我将检查标题,看看我们真正得到了什么......

$ curl -I "http://download.mozilla.org/?product=firefox-3.6.3&os=win&lang=en-US"
HTTP/1.1 302 Found
Server: Apache
X-Backend-Server: pp-app-dist09
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, private
Content-Type: text/html; charset=UTF-8
Date: Sat, 08 May 2010 21:02:50 GMT
Location: http://mozilla.mirror.ac.za/firefox/releases/3.6.3/win32/en-US/Firefox Setup 3.6.3.exe
Pragma: no-cache
Transfer-Encoding: chunked
Connection: Keep-Alive
Set-Cookie: dmo=10.8.84.200.1273352570769772; path=/; expires=Sun, 08-May-11 21:02:50 GMT
X-Powered-By: PHP/5.1.6

所以这实际上是一个 302 重定向,所以现在使用 Location 标头中的内容作为您的新 url 来获取实际文件。您需要弄清楚如何发出请求并自行阅读标题(抱歉,我没有太多时间)。解析位置标头后,您可以使用正则表达式删除位置的其余部分以获取文件名以将文件保存到:

>>> location = 'http://mozilla.mirror.ac.za/firefox/releases/3.6.3/win32/en-US/Firefox Setup 3.6.3.exe'
>>> re.match('^.*/(.*?)$', location).groups()[0]
'Firefox Setup 3.6.3.exe'

因此,要获得实际的文件名,您需要自己遵循 302。我将留给您所需的代码,但希望这将为您指明正确的方向。

于 2010-05-08T21:15:19.347 回答
0
import shutil
import urllib.parse
import urllib.request
import os

urls = {
    'just_filename' : 'https://github.com/bits4waves/100daysofpractice-dataset/raw/master/requirements.txt',
    'filename_with_params' : 'https://github.com/bits4waves/resonometer/blob/master/sound/violin-A-pluck.wav?raw=true',
    'no_filename' : 'https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64&lang=en-US',
}

for url in urls.values():
    with urllib.request.urlopen(url) as response:
        parsed_url_path = urllib.parse.urlparse(response.url).path
        filename = os.path.basename(parsed_url_path)
        with open(filename, 'w+b') as f:
            shutil.copyfileobj(response, f)
于 2021-05-14T17:44:22.007 回答