Pythonurllib.request下载文件教程
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学习网公众号!

- 上一篇
- JS实现进度条的多种方式解析

- 下一篇
- 印象笔记DeepSeek:智能归档与检索攻略
-
- 文章 · python教程 | 12分钟前 |
- PyCharm切换英文界面教程
- 457浏览 收藏
-
- 文章 · python教程 | 21分钟前 |
- Python解析XML:ElementTree使用指南
- 386浏览 收藏
-
- 文章 · python教程 | 27分钟前 |
- Python文本摘要方法及关键信息提取技巧
- 340浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python字典value是什么?如何获取值?
- 445浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- PythonDataFrame合并:concat与merge对比解析
- 222浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python连接PostgreSQL教程:psycopg2使用指南
- 424浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 125次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 122次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 136次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 131次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 132次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览