当前位置:首页 > 文章列表 > 文章 > python教程 > Pythonurllib.request下载文件教程

Pythonurllib.request下载文件教程

2025-08-08 08:42:50 0浏览 收藏

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方法

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.URLErrorurllib.error.HTTPErrorURLError 通常是网络连接问题(比如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头,比如 RefererCookie 等等,这对于模拟更复杂的客户端行为非常有用。

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 参数传递给 urlopenurlretrieve 就可以绕过证书验证。再次强调,除非你非常清楚你在做什么,并且是在受控环境,否则不要轻易禁用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_openerinstall_opener,你可以构建一个定制化的 opener,它包含了各种处理器,比如代理处理器、HTTP Basic Auth 处理器等。一旦安装,后续的 urlopenurlretrieve 调用都会使用这个 opener。这提供了一个非常强大的机制来定制 urllib.request 的行为。

总的来说,urllib.request 在Python标准库中提供了相当全面的文件下载能力。从简单的 urlretrieve 到更复杂的 Request 对象和 OpenerDirector,它能满足大部分日常和进阶的下载需求。关键在于根据实际场景选择合适的工具和策略,特别是要做好错误处理和资源管理。

今天关于《Pythonurllib.request下载文件教程》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于异常处理,文件下载,urllib.request,urlretrieve,urlopen的内容请关注golang学习网公众号!

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