在 Python 3 中从 Web 下载文件
Posted
技术标签:
【中文标题】在 Python 3 中从 Web 下载文件【英文标题】:Download file from web in Python 3 【发布时间】:2011-11-06 19:25:33 【问题描述】:我正在创建一个程序,该程序将通过读取同一游戏/应用程序的 .jad 文件中指定的 URL,从 Web 服务器下载 .jar (java) 文件。我正在使用 Python 3.2.1
我已经成功地从 JAD 文件中提取了 JAR 文件的 URL(每个 JAD 文件都包含指向 JAR 文件的 URL),但正如您可能想象的那样,提取的值是 type() 字符串。
下面是相关函数:
def downloadFile(URL=None):
import httplib2
h = httplib2.Http(".cache")
resp, content = h.request(URL, "GET")
return content
downloadFile(URL_from_file)
但是我总是得到一个错误,说上面函数中的类型必须是字节,而不是字符串。我尝试过使用 URL.encode('utf-8') 和 bytes(URL,encoding='utf-8'),但我总是会遇到相同或类似的错误。
所以基本上我的问题是,当 URL 以字符串类型存储时,如何从服务器下载文件?
【问题讨论】:
@alvas,这个赏金?回答者仍然(并且非常)活跃在 SO 上。为什么不直接添加评论并询问? 因为一个经得起时间考验的好答案值得奖励。此外,我们应该开始对许多其他问题进行此操作,以检查答案是否与今天相关。特别是当 SO 答案的排序相当疯狂时,有时过时甚至最差的答案会排在最前面。 【参考方案1】:如果你想将网页内容获取到变量中,只需read
urllib.request.urlopen
的响应即可:
import urllib.request
...
url = 'http://example.com/'
response = urllib.request.urlopen(url)
data = response.read() # a `bytes` object
text = data.decode('utf-8') # a `str`; this step can't be used if data is binary
下载和保存文件最简单的方法是使用urllib.request.urlretrieve
函数:
import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
urllib.request.urlretrieve(url, file_name)
import urllib.request
...
# Download the file from `url`, save it in a temporary directory and get the
# path to it (e.g. '/tmp/tmpb48zma.txt') in the `file_name` variable:
file_name, headers = urllib.request.urlretrieve(url)
但请记住,urlretrieve
被视为 legacy 并且可能会被弃用(但不知道为什么)。
因此,最正确的方法是使用urllib.request.urlopen
函数返回代表HTTP 响应的类文件对象,然后使用shutil.copyfileobj
将其复制到真实文件中.
import urllib.request
import shutil
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
如果这看起来太复杂,您可能希望更简单,将整个下载存储在 bytes
对象中,然后将其写入文件。但这仅适用于小文件。
import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
data = response.read() # a `bytes` object
out_file.write(data)
可以即时提取.gz
(可能还有其他格式)压缩数据,但这样的操作可能需要 HTTP 服务器支持对文件的随机访问。
import urllib.request
import gzip
...
# Read the first 64 bytes of the file inside the .gz archive located at `url`
url = 'http://example.com/something.gz'
with urllib.request.urlopen(url) as response:
with gzip.GzipFile(fileobj=response) as uncompressed:
file_header = uncompressed.read(64) # a `bytes` object
# Or do anything shown above using `uncompressed` instead of `response`.
【讨论】:
你可以使用response.info().get_param('charset', 'utf-8')
而不是硬编码utf-8
,从Content-Type
标头获取字符编码
@OlehPrypin 为什么outfile.write(data)
只适用于小文件?
“urlretrieve 被认为是遗留的,可能会被弃用”你从哪里得到这个想法?
@Corey: Right from the docs: "21.6.24. Legacy interface 以下函数和类是从 Python 2 模块 urllib(与 urllib2 相对)移植而来的。它们可能会在某些时候被弃用指向未来。” ...我同意 Oleh 的“不知道为什么”
@Oleh Prypin 如果我使用 urllib.request.urlopen(url) 作为响应,open(file_name, 'wb') as out_file: shutil.copyfileobj(response, out_file) 那么我该如何找到catch 语句中的 HTTP 状态代码是否知道文件未找到?【参考方案2】:
每当我想要一些与 HTTP 请求相关的东西时,我都会使用 requests
包,因为它的 API 很容易上手:
首先,安装requests
$ pip install requests
然后是代码:
from requests import get # to make GET request
def download(url, file_name):
# open in binary mode
with open(file_name, "wb") as file:
# get request
response = get(url)
# write to file
file.write(response.content)
【讨论】:
【参考方案3】:我希望我理解的问题是正确的,即:当URL存储为字符串类型时,如何从服务器下载文件?
我使用以下代码下载文件并将其保存在本地:
import requests
url = 'https://www.python.org/static/img/python-logo.png'
fileName = 'D:\Python\dwnldPythonLogo.png'
req = requests.get(url)
file = open(fileName, 'wb')
for chunk in req.iter_content(100000):
file.write(chunk)
file.close()
【讨论】:
嗨,我也使用相同类型的代码来下载文件,但有时我遇到了异常,例如 - 'charmap' 编解码器无法编码字符 '\u010c'..... 你能帮我解决这个问题【参考方案4】:这里我们可以使用 Python3 中 urllib 的 Legacy 接口:
以下函数和类是从 Python 2 模块 urllib(相对于 urllib2)移植而来的。它们可能会在将来的某个时候被弃用。
示例(2行代码):
import urllib.request
url = 'https://www.python.org/static/img/python-logo.png'
urllib.request.urlretrieve(url, "logo.png")
【讨论】:
这样的功能是否有理由被弃用?除了手动将响应写入文件之外,还有其他选择吗? 这太容易了,他们宁愿你自己动手。【参考方案5】:您可以使用 wget,这是一种流行的下载 shell 工具。 https://pypi.python.org/pypi/wget 这将是最简单的方法,因为它不需要打开目标文件。这是一个例子。
import wget
url = 'https://i1.wp.com/python3.codes/wp-content/uploads/2015/06/Python3-powered.png?fit=650%2C350'
wget.download(url, '/Users/scott/Downloads/cat4.jpg')
【讨论】:
这真的很简单【参考方案6】:是的,确实 requests 是一个很好的包,可以用于与 HTTP 请求相关的东西。但是我们需要注意传入数据的编码类型,下面是一个解释差异的示例
from requests import get
# case when the response is byte array
url = 'some_image_url'
response = get(url)
with open('output', 'wb') as file:
file.write(response.content)
# case when the response is text
# Here unlikely if the reponse content is of type **iso-8859-1** we will have to override the response encoding
url = 'some_page_url'
response = get(url)
# override encoding by real educated guess as provided by chardet
r.encoding = r.apparent_encoding
with open('output', 'w', encoding='utf-8') as file:
file.write(response.content)
【讨论】:
到目前为止我看到的对初学者最友好的方法。【参考方案7】:动机
有时,我们想要获取图片但不需要将其下载到真实文件中,
即下载数据并将其保存在内存中。
例如,如果我使用机器学习方法,训练一个可以识别带有数字(条形码)的图像的模型。
当我抓取一些网站并包含这些图像以便我可以使用模型来识别它时,
我不想将这些图片保存在我的磁盘驱动器上,
那么您可以尝试以下方法来帮助您将下载数据保存在内存中。
积分
import requests
from io import BytesIO
response = requests.get(url)
with BytesIO as io_obj:
for chunk in response.iter_content(chunk_size=4096):
io_obj.write(chunk)
基本上就像@Ranvijay Kumar
一个例子
import requests
from typing import NewType, TypeVar
from io import StringIO, BytesIO
import matplotlib.pyplot as plt
import imageio
URL = NewType('URL', str)
T_IO = TypeVar('T_IO', StringIO, BytesIO)
def download_and_keep_on_memory(url: URL, headers=None, timeout=None, **option) -> T_IO:
chunk_size = option.get('chunk_size', 4096) # default 4KB
max_size = 1024 ** 2 * option.get('max_size', -1) # MB, default will ignore.
response = requests.get(url, headers=headers, timeout=timeout)
if response.status_code != 200:
raise requests.ConnectionError(f'response.status_code')
instance_io = StringIO if isinstance(next(response.iter_content(chunk_size=1)), str) else BytesIO
io_obj = instance_io()
cur_size = 0
for chunk in response.iter_content(chunk_size=chunk_size):
cur_size += chunk_size
if 0 < max_size < cur_size:
break
io_obj.write(chunk)
io_obj.seek(0)
""" save it to real file.
with open('temp.png', mode='wb') as out_f:
out_f.write(io_obj.read())
"""
return io_obj
def main():
headers =
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Host': 'statics.591.com.tw',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'
io_img = download_and_keep_on_memory(URL('http://statics.591.com.tw/tools/showPhone.php?info_data=rLsGZe4U%2FbphHOimi2PT%2FhxTPqI&type=rLEFMu4XrrpgEw'),
headers, # You may need this. Otherwise, some websites will send the 404 error to you.
max_size=4) # max loading < 4MB
with io_img:
plt.rc('axes.spines', top=False, bottom=False, left=False, right=False)
plt.rc(('xtick', 'ytick'), color=(1, 1, 1, 0)) # same of plt.axis('off')
plt.imshow(imageio.imread(io_img, as_gray=False, pilmode="RGB"))
plt.show()
if __name__ == '__main__':
main()
【讨论】:
【参考方案8】:如果您使用的是 Linux,您可以通过 python shell 使用 Linux 的wget
模块。这是一个示例代码sn-p
import os
url = 'http://www.example.com/foo.zip'
os.system('wget %s'%url)
【讨论】:
【参考方案9】:from urllib import request
def get(url):
with request.urlopen(url) as r:
return r.read()
def download(url, file=None):
if not file:
file = url.split('/')[-1]
with open(file, 'wb') as f:
f.write(get(url))
【讨论】:
以上是关于在 Python 3 中从 Web 下载文件的主要内容,如果未能解决你的问题,请参考以下文章
openCV - 如何在 Windows 8.1 中从为 github 下载的用于 python 的 .zip 文件重建?