Python Requests 模块异常处理完全指南:从入门到实战

在使用 Python 进行网络编程时,requests 模块无疑是我们手中的利剑。它简单、优雅,让发起 HTTP 请求变得像呼吸一样自然。然而,网络世界充满了不确定性——服务器可能会宕机,链接可能会失效,甚至是一次简单的超时都可能导致我们的脚本崩溃。因此,掌握 requests 模块的异常处理机制,不仅仅是为了防止程序报错,更是为了构建健壮、可靠的应用程序。

在这篇文章中,我们将深入探讨如何使用 INLINECODEc725f521 和 INLINECODEa5cd6c89 块来优雅地处理请求过程中可能遇到的各种“坑”。我们将从最基础的概念开始,逐步深入到处理复杂的网络错误和超时问题。通过一系列实用的代码示例,我们将学会如何识别、捕获并响应这些异常,确保我们的程序在面对不可预测的网络环境时依然能够从容应对。

核心概念回顾

在开始“抓虫”之前,我们需要先了解一下 requests 模块中用于检查响应状态的关键属性和方法。这是判断请求是否成功的第一道防线。

  • r.status_code: 这是一个数字,它告诉我们服务器返回的状态。比如 200 表示一切正常,404 表示页面未找到,500 则通常是服务器内部出了问题。
  • r.url: 这是最终返回响应的 URL。有时候,请求可能会经过重定向,这个属性能让我们看到最终的落脚点。
  • INLINECODEb6de2fa8: 这是一个非常有用的方法。如果请求成功(状态码 200),它什么也不会做;但如果请求出现了 4xx 或 5xx 错误,它会抛出一个 INLINECODE764c333f 异常。这在结合 try...except 使用时特别强大。
  • r.request: 这个属性包含了我们发出的原始请求对象,方便我们在出错时回溯查看我们到底请求了什么。

成功的连接:理想情况

首先,让我们看看最理想的情况。当我们向一个稳定的服务器(比如 Google)发起请求时,一切都会很顺利。

import requests

# 发起一个简单的 GET 请求
try:
    r = requests.get("https://www.google.com/")
    # 检查状态码,如果是 200 则表示成功
    print(f"连接成功,状态码:{r.status_code}")
except Exception as e:
    print(f"发生未知错误:{e}")

在这个例子中,如果网络通畅,你将看到状态码 200。这是所有 HTTP 客户端最期待看到的数字。

处理 HTTP 错误 (HTTPError)

现实往往不那么完美。很多时候,我们会遇到 404(页面不存在)或者 500(服务器错误)。如果我们不处理这些错误,程序可能会继续运行并产生不可预知的结果,或者直接打印出一堆丑陋的 Traceback。

让我们尝试访问一个亚马逊上不存在的页面,并使用 raise_for_status() 来触发异常。

import requests

url = "https://www.amazon.com/nothing_here"

try:
    # 发起请求,设置超时时间为 1 秒
    r = requests.get(url, timeout=1)
    # 这一行会检查状态码,如果是 4xx 或 5xx,则抛出 HTTPError
    r.raise_for_status()
except requests.exceptions.HTTPError as errh:
    print("捕获到 HTTP 错误:")
    # 打印详细的错误信息,比如 404 Not Found
    print(f"错误详情:{errh.args[0]}")
except Exception as e:
    print(f"其他错误:{e}")

print("
程序继续运行...")

代码解析:

在这个例子中,当 INLINECODE104a23da 发现状态码是 404 时,它会立即停止 INLINECODE77c6240f 块的执行,并跳转到 except requests.exceptions.HTTPError 块。这样,我们就可以优雅地记录错误,或者给用户展示一个友好的提示,而不是让程序崩溃。

通用异常处理

在 INLINECODE5ab053e1 库中,有一个基类异常叫做 INLINECODEf554346a。几乎所有 requests 引发的异常都继承自它。这意味着,如果你暂时不想区分具体的错误类型(是连接超时?还是 DNS 解析失败?),你可以直接捕获这个通用异常。这在编写脚本快速原型时非常有用。

url = "https://www.google.com/"
try:
    r = requests.get(url, timeout=1)
    r.raise_for_status()
    print("请求成功!")
except requests.exceptions.RequestException as errex:
    # 这里会捕获几乎所有与 requests 相关的错误
    print("Oops, 请求过程中发生了一个异常:")
    print(errex)

深入理解超时

在网络请求中,超时 是一个必须重视的话题。想象一下,你的程序在等待一个永远都不会到来的响应。如果没有设置超时,你的程序可能会永远卡在那里。

INLINECODE7649cfc3 允许我们通过 INLINECODE1648c193 参数来设定等待时间。我们可以捕获 ReadTimeout 异常来处理服务器响应太慢的情况。

import requests

url = "https://www.google.com/"
try:
    # 设置超时为 1 秒
    # 注意:即使是 Google,在某些网络环境下 1 秒也可能不够,这取决于你的网络速度
    r = requests.get(url, timeout=1)
    r.raise_for_status()
except requests.exceptions.ReadTimeout as errrt:
    print("读取超时:服务器响应太慢了。")
except requests.exceptions.RequestException as errex:
    print("其他请求错误:")
    print(errex)
else:
    print(f"请求完成,状态码:{r.status_code}")

实战建议:

在生产环境中,我们通常会将 timeout 设置为一个稍大一点的值(比如 3 到 10 秒),或者将其作为配置文件中的一个参数,方便随时调整。如果我们把上面的代码 timeout=0.0001,几乎肯定会触发超时异常,因为没有人能在那微秒级的时间内完成 TCP 握手和数据传输。

捕获 URL 格式错误

有时候,错误是由于我们自己手滑造成的。比如,忘记在 URL 前面加上 INLINECODEfe76e765 或 INLINECODEafa6edf5 协议头。

url = "www.google.com" # 这是一个错误的 URL,缺少协议头
try:
    r = requests.get(url, timeout=1)
    r.raise_for_status()
except requests.exceptions.MissingSchema as errmiss:
    print("URL 格式错误:")
    print("请确保 URL 以 http:// 或 https:// 开头。")
except requests.exceptions.ReadTimeout as errrt:
    print("请求超时")

这种错误通常发生在配置文件读取 URL 或者用户输入 URL 的场景下。提前捕获 MissingSchema 可以给用户非常明确的反馈。

处理连接错误

还有一种情况是物理层面的连接问题。比如,我们要访问的域名根本不存在(DNS 解析失败),或者服务器拒绝了连接,或者是你的网络断了。这时候会抛出 ConnectionError

# 假设我们要访问一个根本不存在的本地地址,或者被防火墙拦截的地址
url = "http://thisdefinitelydoesnotexist12345.com"

try:
    r = requests.get(url, timeout=5)
    r.raise_for_status()
except requests.exceptions.HTTPError as errh:
    print("HTTP 错误")
except requests.exceptions.ConnectionError as conerr:
    print("连接错误:")
    print("无法连接到服务器。请检查您的网络连接,或确认域名是否正确。")
except requests.exceptions.RequestException as errex:
    print("其他异常")

整合:构建健壮的网络请求客户端

现在,让我们把学到的知识整合起来。在实际开发中,我们通常会有一个封装好的请求函数,它能够处理各种常见的异常。

下面的代码展示了一个完整的异常处理流程,遵循了“从具体到一般”的原则。我们先捕获具体的异常(如超时、HTTP错误),最后捕获通用的异常。这样做的好处是,我们可以针对不同的错误采取不同的恢复策略。

import requests

def make_robust_request(url):
    print(f"正在尝试访问:{url}")
    try:
        # verify=True 启用 SSL 证书验证(默认行为,这里是显式声明)
        # timeout=1 设置连接和读取的超时时间
        r = requests.get(url, timeout=5, verify=True)
        
        # 如果状态码不是 200,这里会抛出 HTTPError
        r.raise_for_status()
        
    # 1. 处理 HTTP 协议层面的错误 (404, 500 等)
    except requests.exceptions.HTTPError as errh:
        print(" -> [HTTP 错误]")
        print(f"    状态码:{errh.response.status_code}")
        print(f"    详情:{errh.args[0]}")
        return None

    # 2. 处理连接超时或读取超时
    except requests.exceptions.Timeout as errt:
        print(" -> [超时错误]")
        print("    服务器响应时间过长,连接已断开。")
        return None

    # 3. 处理连接错误 (DNS 失败, 连接被拒绝等)
    except requests.exceptions.ConnectionError as conerr:
        print(" -> [连接错误]")
        print("    网络似乎有问题,或者服务器不存在。")
        return None

    # 4. 处理 requests 库抛出的所有其他异常
    except requests.exceptions.RequestException as errex:
        print(" -> [严重异常]")
        print(f"    意外的错误:{errex}")
        return None
    
    # 5. 如果一切顺利
    else:
        print(" -> [成功]")
        print(f"    返回数据大小:{len(r.content)} 字节")
        return r

# --- 测试我们的函数 ---

# 测试用例 1: 一个正常的网站
print("
测试用例 1 (正常):")
make_robust_request("https://www.google.com/")

# 测试用例 2: 一个不存在的页面 (404)
print("
测试用例 2 (404):")
make_robust_request("https://www.google.com/asdfasdf")

# 测试用例 3: 一个无效的 URL
print("
测试用例 3 (无效 URL):")
make_robust_request("google.com") 

最佳实践与性能优化建议

在结束这篇文章之前,我想分享几点在实际开发中非常有用的经验:

  • 始终设置 timeout:这是最重要的一条。如果不设置 timeout,你的线程可能会无限期地挂起。根据你的业务需求,通常建议将 timeout 设置在 3 到 10 秒之间。
  • 使用会话对象:如果你需要向同一个主机发起多个请求(比如爬取一个网站的多个页面),使用 requests.Session()。它会复用底层的 TCP 连接,这被称为 HTTP Keep-Alive。这能显著提高性能并减少网络延迟。
  • 重试机制:网络是不稳定的。有时候,一个请求仅仅是因为瞬间的网络抖动就失败了。在生产环境中,不要直接放弃,而是实现一个重试机制(比如使用 retrying 库或者编写自己的循环逻辑),特别是在遇到 5xx 服务器错误或连接超时的时候。
  • 关闭连接:当你使用了 Session 对象时,记得在任务完成后使用 INLINECODE913c4eec,或者使用 INLINECODEfd60f91d 上下文管理器(with requests.Session() as session:)来自动管理资源的释放。

总结

处理异常并不是为了掩盖错误,而是为了让我们有机会去修正错误或者优雅地通知用户。通过合理运用 requests.exceptions 下的各种异常类,我们可以将脆弱的脚本转化为稳健的网络服务。

现在,你已经掌握了如何应对网络编程中最常见的挑战。试着把你现有的项目拿出来检查一下,看看那些裸露的 requests.get() 是不是也该穿上“盔甲”了呢?

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/33714.html
点赞
0.00 平均评分 (0% 分数) - 0