Pythonurllib.request下载文件教程
时间:2025-08-08 08:42:50 384浏览 收藏
golang学习网今天将给大家带来《Python用urllib.request下载文件方法详解》,感兴趣的朋友请继续看下去吧!以下内容将会涉及到等等知识点,如果你是正在学习文章或者已经是大佬级别了,都非常欢迎也希望大家都能给我建议评论哈~希望能帮助到大家!
对于简单文件下载,使用urllib.request.urlretrieve最直接,一行代码即可完成;2. 需要精细控制时,应使用urllib.request.urlopen配合文件操作,支持分块读取,适合大文件;3. 显示下载进度可通过urlretrieve的reporthook参数或在urlopen的读取循环中手动计算实现;4. 处理网络异常需捕获URLError、HTTPError、timeout等异常,并采用重试机制提高健壮性;5. 定制下载行为可通过Request对象设置请求头、使用ssl._create_unverified_context绕过SSL验证、通过ProxyHandler配置代理。最终应根据场景选择合适方法并做好错误处理和资源管理,以确保下载的稳定性和安全性。

Python要实现文件下载,最直接的方式通常是利用 urllib.request 模块中的 urlretrieve 函数,它简洁高效,适合多数简单场景。但如果需要更精细的控制,比如处理大文件、显示下载进度或者自定义请求头,那么 urlopen 配合文件操作会是更灵活的选择。
解决方案
说实话,对于简单的文件下载任务,urllib.request.urlretrieve 简直是为懒人准备的。它一行代码就能搞定,把远程URL指向的内容直接保存到本地文件。比如,你想下载一张图片或者一个PDF:
import urllib.request
import os
url = "https://www.example.com/some_file.zip" # 替换成你要下载的真实URL
local_filename = "downloaded_file.zip"
try:
print(f"开始下载:{url} 到 {local_filename}")
urllib.request.urlretrieve(url, local_filename)
print("下载完成!")
except Exception as e:
print(f"下载失败:{e}")
if os.path.exists(local_filename):
os.remove(local_filename) # 失败时清理不完整的文件这方法挺好用的,但它有个缺点:你没法实时知道下载进度,也不太好处理超大文件,因为它是把整个文件先下载到内存,再写入磁盘(虽然底层可能做了优化,但概念上是这样)。
所以,如果文件比较大,或者你需要显示下载进度,甚至是自定义请求头什么的,那 urllib.request.urlopen 就派上用场了。它打开的是一个类似文件的对象,你可以一点点地读取内容,然后写入本地文件。这就像是你在水龙头下面接水,可以控制每次接多少,而不是一下子把整个水桶都灌满。
import urllib.request
import os
import shutil # 用于高效地复制文件对象
url = "https://www.example.com/another_large_file.mp4" # 替换成你要下载的真实URL
local_filename = "large_video.mp4"
buffer_size = 8192 # 每次读取的字节数,可以根据需要调整
try:
print(f"开始通过流式下载:{url} 到 {local_filename}")
with urllib.request.urlopen(url) as response, open(local_filename, 'wb') as out_file:
# 更好的方式是使用shutil.copyfileobj,它会处理好缓冲区
shutil.copyfileobj(response, out_file, buffer_size)
print("流式下载完成!")
except Exception as e:
print(f"流式下载失败:{e}")
if os.path.exists(local_filename):
os.remove(local_filename)这种 urlopen 配合 shutil.copyfileobj 的方式,对于大文件下载是更推荐的,因为它避免了一次性加载所有内容到内存,而是分块处理。
处理下载进度与大文件传输:urllib.request的高级应用
说到大文件,最让人头疼的莫过于下载过程中一片寂静,不知道到底下了多少,还有多久能完成。urllib.request.urlretrieve 其实提供了一个 reporthook 参数,可以用来报告下载进度。这是一个回调函数,每次数据块被传输时都会被调用。
import urllib.request
import os
import sys
def download_progress_hook(count, block_size, total_size):
"""
一个简单的下载进度报告函数
:param count: 已经传输的数据块数量
:param block_size: 每个数据块的大小(字节)
:param total_size: 远程文件总大小(字节)
"""
if total_size <= 0: # 有些服务器可能不提供Content-Length
print("文件大小未知,无法显示进度。", end='\r')
return
downloaded_bytes = count * block_size
progress = (downloaded_bytes / total_size) * 100
# 使用 \r 回到行首,实现单行刷新进度
sys.stdout.write(f"\r下载进度: {progress:.2f}% ({downloaded_bytes}/{total_size} 字节)")
sys.stdout.flush()
url_with_progress = "https://www.python.org/static/img/python-logo.png" # 替换成一个稍微大一点的URL测试
local_filename_progress = "python_logo_with_progress.png"
try:
print(f"开始带进度下载:{url_with_progress} 到 {local_filename_progress}")
urllib.request.urlretrieve(url_with_progress, local_filename_progress, reporthook=download_progress_hook)
print("\n带进度下载完成!") # 下载完成后换行
except Exception as e:
print(f"\n带进度下载失败:{e}")
if os.path.exists(local_filename_progress):
os.remove(local_filename_progress)而用 urlopen 的方式,实现进度条就更直接了,因为你本身就是手动读取数据块。你只需要在循环里计算已经读取的字节数,然后更新进度就行。这给了你更大的自由度,比如你可以计算下载速度,或者估算剩余时间。
import urllib.request
import os
import sys
import time
url_large_file = "https://www.example.com/a_very_large_file.zip" # 假定一个大文件URL
local_large_file = "downloaded_very_large_file.zip"
chunk_size = 8192 # 每次读取的块大小
try:
with urllib.request.urlopen(url_large_file) as response:
total_size = int(response.info().get('Content-Length', -1))
downloaded_bytes = 0
start_time = time.time()
with open(local_large_file, 'wb') as out_file:
while True:
chunk = response.read(chunk_size)
if not chunk:
break
out_file.write(chunk)
downloaded_bytes += len(chunk)
# 实时更新进度
if total_size > 0:
progress = (downloaded_bytes / total_size) * 100
elapsed_time = time.time() - start_time
speed = downloaded_bytes / elapsed_time if elapsed_time > 0 else 0
sys.stdout.write(f"\r下载进度: {progress:.2f}% ({downloaded_bytes}/{total_size} 字节) 速度: {speed/1024:.2f} KB/s")
sys.stdout.flush()
else:
sys.stdout.write(f"\r已下载: {downloaded_bytes} 字节 (文件大小未知)")
sys.stdout.flush()
print("\n大文件下载完成!")
except Exception as e:
print(f"\n大文件下载失败:{e}")
if os.path.exists(local_large_file):
os.remove(local_large_file)这种手动控制的模式,虽然代码量稍微多一点,但灵活性是 urlretrieve 无法比拟的。
应对网络异常与下载错误:urllib.request的健壮性考量
网络环境嘛,总是充满了不确定性。断网、服务器宕机、超时、文件不存在,这些都是下载过程中可能遇到的“拦路虎”。所以,编写下载代码,错误处理是必须的。urllib.request 会抛出几种特定的异常,我们应该针对性地捕获它们。
最常见的异常是 urllib.error.URLError 和 urllib.error.HTTPError。URLError 通常是网络连接问题(比如DNS解析失败、无法连接服务器),而 HTTPError 则是服务器返回了错误状态码(比如404 Not Found, 500 Internal Server Error)。还有 socket.timeout,如果设置了超时时间的话。
import urllib.request
import urllib.error # 导入错误模块
import socket # 用于捕获超时异常
import os
def download_robustly(url, filename, retries=3, timeout=10):
"""
尝试多次下载,处理常见网络错误
"""
for attempt in range(retries):
try:
print(f"尝试下载 (第 {attempt + 1} 次): {url}")
# 创建一个Request对象,可以设置超时
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=timeout) as response, open(filename, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
print(f"下载成功: {filename}")
return True # 成功则退出
except urllib.error.HTTPError as e:
print(f"HTTP错误: {e.code} - {e.reason}")
if e.code == 404: # 文件不存在,重试也没用
print("文件不存在,停止重试。")
break
except urllib.error.URLError as e:
print(f"URL错误: {e.reason}")
except socket.timeout:
print("连接超时。")
except Exception as e:
print(f"发生未知错误: {e}")
print("等待几秒后重试...")
time.sleep(2 ** attempt) # 指数退避,等待时间逐渐增加
print(f"所有尝试失败,无法下载: {url}")
if os.path.exists(filename):
os.remove(filename) # 失败时清理不完整文件
return False
# 测试用例
# download_robustly("http://httpbin.org/status/404", "not_found.html") # 测试404
# download_robustly("http://nonexistent-domain-12345.com/file.txt", "domain_error.txt") # 测试URLError
# download_robustly("http://httpbin.org/delay/15", "timeout_test.html", timeout=5) # 测试超时
download_robustly("https://www.example.com/index.html", "example_index.html") # 正常下载在代码里加入 try...except 块,并根据不同的异常类型做不同的处理,比如对于 404 这种明确表示资源不存在的错误,就没必要重试了。而对于超时或者临时的网络波动,重试几次通常能解决问题。同时,确保文件句柄在使用完毕后关闭,with 语句在这方面做得非常好,它能保证资源被正确释放,哪怕中间出了错。
定制下载行为:请求头、代理与SSL验证的配置技巧
很多时候,简单的下载并不够。网站可能会检查你的 User-Agent,或者需要通过特定的代理才能访问,又或者SSL证书验证出了问题。urllib.request 提供了 Request 对象和 OpenerDirector 来处理这些高级需求。
1. 添加自定义请求头 (Headers)
最常见的需求就是设置 User-Agent,模拟浏览器访问,避免被服务器拒绝。你可以在创建 Request 对象时传入一个字典。
import urllib.request
import os
url_with_headers = "https://httpbin.org/headers" # 这个URL会返回你请求的头部信息
local_header_info = "headers_response.json"
try:
# 创建一个Request对象,并添加User-Agent头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
}
req = urllib.request.Request(url_with_headers, headers=headers)
with urllib.request.urlopen(req) as response, open(local_header_info, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
print(f"带自定义头下载完成:{local_header_info}")
except Exception as e:
print(f"带自定义头下载失败:{e}")
if os.path.exists(local_header_info):
os.remove(local_header_info)通过 Request 对象,你可以添加任何你想要的HTTP头,比如 Referer、Cookie 等等,这对于模拟更复杂的客户端行为非常有用。
2. 处理SSL验证问题
有时候,你会遇到SSL证书验证失败的错误,尤其是在测试环境或者自签名证书的网站。虽然不推荐在生产环境中禁用SSL验证(因为这会降低安全性),但在特定测试场景下,你可能需要这么做。
import urllib.request
import ssl # 导入ssl模块
import os
url_bad_ssl = "https://self-signed.badssl.com/" # 一个故意使用无效SSL证书的网站
local_bad_ssl_content = "bad_ssl_content.html"
# 默认情况下,urllib会验证SSL证书,这个URL会失败
try:
print("尝试默认SSL验证下载 (预期失败):")
urllib.request.urlretrieve(url_bad_ssl, local_bad_ssl_content)
print("默认SSL下载成功 (不应该发生)。")
except urllib.error.URLError as e:
print(f"默认SSL下载失败 (预期): {e.reason}")
if os.path.exists(local_bad_ssl_content):
os.remove(local_bad_ssl_content)
# 禁用SSL证书验证
try:
print("\n尝试禁用SSL验证下载:")
# 创建一个不验证SSL证书的上下文
context = ssl._create_unverified_context()
# 使用这个上下文进行下载
urllib.request.urlretrieve(url_bad_ssl, local_bad_ssl_content, context=context)
print(f"禁用SSL验证下载成功:{local_bad_ssl_content}")
except Exception as e:
print(f"禁用SSL验证下载失败:{e}")
if os.path.exists(local_bad_ssl_content):
os.remove(local_bad_ssl_content)使用 ssl._create_unverified_context() 会创建一个不进行证书验证的SSL上下文。把它作为 context 参数传递给 urlopen 或 urlretrieve 就可以绕过证书验证。再次强调,除非你非常清楚你在做什么,并且是在受控环境,否则不要轻易禁用SSL验证。
3. 配置代理 (Proxy)
虽然不是所有下载都需要代理,但在某些受限网络环境下,或者需要匿名访问时,设置代理是必要的。urllib.request 允许你通过 ProxyHandler 来实现。
import urllib.request
import os
# 假设你有一个HTTP代理服务器
# proxy_url = "http://your_proxy_ip:your_proxy_port" # 替换为你的代理地址
# 如果你需要使用代理,可以这样配置:
# try:
# print("\n尝试通过代理下载:")
# # 创建代理处理器
# proxy_handler = urllib.request.ProxyHandler({'http': proxy_url, 'https': proxy_url})
# # 创建一个opener,它将使用这个代理处理器
# opener = urllib.request.build_opener(proxy_handler)
# # 安装这个opener,使其成为全局默认
# urllib.request.install_opener(opener)
# # 现在所有的urllib.request操作都会通过这个代理
# proxy_test_url = "https://www.example.com"
# local_proxy_content = "example_via_proxy.html"
# urllib.request.urlretrieve(proxy_test_url, local_proxy_content)
# print(f"通过代理下载成功:{local_proxy_content}")
# except Exception as e:
# print(f"通过代理下载失败:{e}")
# if os.path.exists(local_proxy_content):
# os.remove(local_proxy_content)
print("\n(代理配置示例已注释,请根据需要取消注释并配置您的代理地址)")通过 build_opener 和 install_opener,你可以构建一个定制化的 opener,它包含了各种处理器,比如代理处理器、HTTP Basic Auth 处理器等。一旦安装,后续的 urlopen 或 urlretrieve 调用都会使用这个 opener。这提供了一个非常强大的机制来定制 urllib.request 的行为。
总的来说,urllib.request 在Python标准库中提供了相当全面的文件下载能力。从简单的 urlretrieve 到更复杂的 Request 对象和 OpenerDirector,它能满足大部分日常和进阶的下载需求。关键在于根据实际场景选择合适的工具和策略,特别是要做好错误处理和资源管理。
今天关于《Pythonurllib.request下载文件教程》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于异常处理,文件下载,urllib.request,urlretrieve,urlopen的内容请关注golang学习网公众号!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
423 收藏
-
297 收藏
-
286 收藏
-
174 收藏
-
319 收藏
-
294 收藏
-
345 收藏
-
464 收藏
-
243 收藏
-
490 收藏
-
441 收藏
-
302 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习