当前位置: 首页 > 工具软件 > Requests > 使用案例 >

python requests请求失败重试_吐血总结,Python Requests库使用指南

谢昂雄
2023-12-01

让我们首先安装requests库。为此,请运行以下命令:

pip install requests

如果你喜欢使用 Pipenv 管理Python包,你可以运行下面的命令:

pipenv install requests

一旦安装了requests,你就可以在应用程序中使用它。像这样导入requests:

import requests

现在你已经都准备完成了,那么是时候开始使用requests的旅程了。你的第一个目标是学习如何发出GET请求。

GET 请求

HTTP方法(如GET和POST)决定当发出HTTP请求时尝试执行的操作。除了GET和POST之外,还有其他一些常用的方法,你将在本教程的后面部分使用到。

最常见的HTTP方法之一是GET。GET方法表示你正在尝试从指定资源获取或检索数据。要发送GET请求,请调用requests.get()。

你可以通过下面方式来向GitHub的 Root REST API 发出GET请求:

>>> requests.get( https://api.github.com )

恭喜!你发出了你的第一个请求。接下来让我们更深入地了解该请求的响应。

响应

Response是检查请求结果的强有力的对象。让我们再次发出相同的请求,但这次将返回值存储在一个变量中,以便你可以仔细查看其属性和方法:

>>> response = requests.get( https://api.github.com )

在此示例中,你捕获了get()的返回值,该值是Response的实例,并将其存储在名为response的变量中。你现在可以使用response来查看有关GET请求结果的全部信息。

状态码

您可以从Response获取的第一部分信息是状态码。状态码会展示你请求的状态。

例如,200OK状态表示你的请求成功,而404NOT FOUND状态表示找不到你要查找的资源。还有许多其它的状态码 ,可以为你提供关于你的请求所发生的具体情况。

通过访问.status_code,你可以看到服务器返回的状态码:

>>> response.status_code

200

.status_code返回200意味着你的请求是成功的,并且服务器返回你要请求的数据。

有时,你可能想要在代码中使用这些信息来做判断:

if response.status_code == 200:

print( Success! )

elif response.status_code == 404:

print( Not Found. )

按照这个逻辑,如果服务器返回200状态码,你的程序将打印Success!如果结果是404,你的程序将打印NotFound.。

requests更进一步为你简化了此过程。如果在条件表达式中使用Response实例,则在状态码介于200和400之间时将被计算为为True,否则为False。

因此,你可以通过重写if语句来简化上一个示例:

if response:

print( Success! )

else:

print( An error has occurred. )

技术细节: 因为__bool__()是Response上的重载方法 ,因此真值测试才成立。

这意味着重新定义了Response的默认行为,用来在确定对象的真值时考虑状态码。

技术细节: 因为__bool__()是Response上的重载方法 ,因此真值测试才成立。

这意味着重新定义了Response的默认行为,用来在确定对象的真值时考虑状态码。

请记住,此方法不会验证状态码是否等于200。原因是200到400范围内的其他状态代码,例如204NO CONTENT和304NOT MODIFIED,就意义而言也被认为是成功的响应。

例如,204告诉你响应是成功的,但是下消息体中没有返回任何内容。

因此,通常如果你想知道请求是否成功时,请确保使用这方便的简写,然后在必要时根据状态码适当地处理响应。

假设你不想在if语句中检查响应的状态码。相反,如果请求不成功,你希望抛出一个异常。你可以使用.raise_for_status()执行此操作:

import requests

from requests.exceptions import HTTPError

for url in [ https://api.github.com , https://api.github.com/invalid ]:

try:

response = requests.get(url)

# If the response was successful, no Exception will be raised

response.raise_for_status()

except HTTPError as http_err:

print(f HTTP error occurred: {http_err} ) # Python 3.6

except Exception as err:

print(f Other error occurred: {err} ) # Python 3.6

else:

print( Success! )

如果你调用.raise_for_status(),将针对某些状态码引发HTTPError异常。如果状态码指示请求成功,则程序将继续进行而不会引发该异常。

进一步阅读:如果你不熟悉Python 3.6的 f-strings,我建议你使用它们,因为它们是简化格式化字符串的好方法。

进一步阅读:如果你不熟悉Python 3.6的 f-strings,我建议你使用它们,因为它们是简化格式化字符串的好方法。

现在,你对于如何处理从服务器返回的响应的状态码了解了许多。但是,当你发出GET请求时,你很少只关心响应的状态码。通常,你希望看到更多。接下来,你将看到如何查看服务器在响应正文中返回的实际数据。

响应内容

GET请求的响应通常在消息体中具有一些有价值的信息,称为有效负载。使用Response的属性和方法,你可以以各种不同的格式查看有效负载。

要以 字节 格式查看响应的内容,你可以使用.content:

>>> response = requests.get( https://api.github.com )

>>> response.content

b {"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}

虽然.content允许你访问响应有效负载的原始字节,但你通常希望使用 UTF-8 等字符编码将它们转换为字符串。当你访问.text时,response将为你执行此操作:

>>> response.text

{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}

因为对bytes解码到str需要一个编码格式,所以如果你没有指定,请求将尝试根据响应头来猜测编码格式。你也可以在访问.text之前通过.encoding来显式设置编码:

>>> response.encoding = utf-8 # Optional: requests infers this internally

>>> response.text

{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}

如果你看看响应,你会发现它实际上是序列化的JSON内容。要获取字典内容,你可以使用.text获取str并使用json.loads()对其进行反序列化。但是,完成此任务的更简单方法是使用.json():

>>> response.json()

{ current_user_url : https://api.github.com/user , current_user_authorizations_html_url : https://github.com/settings/connections/applications{/client_id} ...} }

.json()返回值的类型是字典类型,因此你可以使用键值对的方式访问对象中的值。

你可以使用状态码和消息体做许多事情。但是,如果你需要更多信息,例如有关response本身的元数据,则需要查看响应头部。

响应头部

响应头部可以为你提供有用的信息,例如响应有效负载的内容类型以及缓存响应的时间限制。要查看这些头部,请访问.headers:

>>> response.headers

{ Server : GitHub.com , Date : Mon, 10 Dec 2018 17:49:54 GMT , Content-Type : application/json; charset=utf-8 ,...}

.headers返回类似字典的对象,允许你使用键来获取头部中的值。例如,要查看响应有效负载的内容类型,你可以访问Content-Type:

>>> response.headers[ Content-Type ]

application/json; charset=utf-8

但是,这个类似于字典的头部对象有一些特别之处。HTTP规范定义头部不区分大小写,这意味着我们可以访问这些头信息而不必担心它们的大小写:

>>> response.headers[ content-type ]

application/json; charset=utf-8

无论您使用content-type还是Content-Type,你都将获得相同的值。

现在,你已经学习了有关Response的基础知识。你已经看到了它最有用的属性和方法。让我们退后一步,看看自定义GET请求时你的响应如何变化。

查询字符串参数

自定义GET请求的一种常用方法是通过URL中的 查询字符串 参数传递值。要使用get()执行此操作,请将数据传递给params。例如,你可以使用GitHub的Search API来查找requests库:

import requests

# Search GitHub s repositories for requests

response = requests.get(

https://api.github.com/search/repositories ,

params={ q : requests+language:python },

)

# Inspect some attributes of the `requests` repository

json_response = response.json()

repository = json_response[ items ][0]

print(f Repository name: {repository["name"]} ) # Python 3.6+

print(f Repository deion: {repository["deion"]} ) # Python 3.6+

通过将字典{ q :requests + language:python }传递给.get()的params参数,你可以修改从Search API返回的结果。

你可以像你刚才那样以字典的形式或以元组列表形式将params传递给get():

>>> requests.get(

... https://api.github.com/search/repositories ,

... params=[( q , requests+language:python )],

... )

你甚至可以传bytes作为值:

>>> requests.get(

... https://api.github.com/search/repositories ,

... params=b q=requests+language:python ,

... )

查询字符串对于参数化GET请求很有用。你还可以通过添加或修改发送的请求的头部来自定义你的请求。

请求头

要自定义请求头,你可以使用headers参数将HTTP头部组成的字典传递给get()。例如,你可以通过Accept中指定文本匹配媒体类型来更改以前的搜索请求,以在结果中突出显示匹配的搜索字词:

import requests

response = requests.get(

https://api.github.com/search/repositories ,

params={ q : requests+language:python },

headers={ Accept : application/vnd.github.v3.text-match+json },

)

# View the new `text-matches` array which provides information

# about your search term within the results

json_response = response.json()

repository = json_response[ items ][0]

print(f Text matches: {repository["text_matches"]} )

Accept告诉服务器你的应用程序可以处理哪些内容类型。由于你希望突出显示匹配的搜索词,所以使用的是application/vnd.github.v3.text-match+json,这是一个专有的GitHub的Accept标头,其内容为特殊的JSON格式。

在你了解更多自定义请求的方法之前,让我们通过探索其他HTTP方法来拓宽视野。

其他HTTP方法

除了GET之外,其他流行的HTTP方法包括POST,`PUT,DELETE,HEAD,PATCH和OPTIONS。requests为每个HTTP方法提供了一个方法,与get()`具有类似的结构:

>>> requests.post( https://httpbin.org/post , data={ key : value })

>>> requests.put( https://httpbin.org/put , data={ key : value })

>>> requests.delete( https://httpbin.org/delete )

>>> requests.head( https://httpbin.org/get )

>>> requests.patch( https://httpbin.org/patch , data={ key : value })

>>> requests.options( https://httpbin.org/get )

调用每个函数使用相应的HTTP方法向httpbin服务发出请求。对于每种方法,你可以像以前一样查看其响应:

>>> response = requests.head( https://httpbin.org/get )

>>> response.headers[ Content-Type ]

application/json

>>> response = requests.delete( https://httpbin.org/delete )

>>> json_response = response.json()

>>> json_response[ args ]

{}

每种方法的响应中都会返回头部,响应正文,状态码等。接下来,你将进一步了解POST,`PUT和PATCH方法,并了解它们与其他请求类型的区别。

消息体

根据HTTP规范,POST,`PUT和不太常见的PATCH请求通过消息体而不是通过查询字符串参数传递它们的数据。使用requests,你将有效负载传递给相应函数的data参数。

data接收字典,元组列表,字节或类文件对象。你需要将在请求正文中发送的数据调整为与你交互的服务的特定格式。

例如,如果你的请求的内容类型是application/x-www-form-urlencoded,则可以将表单数据作为字典发送:

>>> requests.post( https://httpbin.org/post , data={ key : value })

你还可以将相同的数据作为元组列表发送:

>>> requests.post( https://httpbin.org/post , data=[( key , value )])

但是,如果需要发送JSON数据,则可以使用json参数。当你通过json传递JSON数据时,requests将序列化你的数据并为你添加正确的Content-Type标头。

httpbin.org 是requests作者 Kenneth Reitz 创建的一个很好的资源。它是一种接收测试请求并响应有关请求数据的服务。例如,你可以使用它来检查基本的POST请求:

>>> response = requests.post( https://httpbin.org/post , json={ key : value })

>>> json_response = response.json()

>>> json_response[ data ]

{"key": "value"}

>>> json_response[ headers ][ Content-Type ]

application/json

你可以从响应中看到服务器在你发送请求时收到了请求数据和标头。requests还以PreparedRequest的形式向你提供此信息。

检查你的请求

当你发出请求时,requests库会在将请求实际发送到目标服务器之前准备该请求。请求准备包括像验证头信息和序列化JSON内容等。

你可以通过访问.request来查看PreparedRequest:

>>> response = requests.post( https://httpbin.org/post , json={ key : value })

>>> response.request.headers[ Content-Type ]

application/json

>>> response.request.url

https://httpbin.org/post

>>> response.request.body

b {"key": "value"}

通过检查PreparedRequest,你可以访问有关正在进行的请求的各种信息,例如有效负载,URL,头信息,身份验证等。

到目前为止,你已经发送了许多不同类型的请求,但它们都有一个共同点:它们是对公共API的未经身份验证的请求。你遇到的许多服务可能都希望你以某种方式进行身份验证。

身份验证

身份验证可帮助服务了解你的身份。通常,你通过将数据传递到Authorization头信息或服务定义的自定义头信息来向服务器提供凭据。你在此处看到的所有请求函数都提供了一个名为auth的参数,允许你传递凭据。

需要身份验证的一个示例API的是GitHub的 Authenticated User API。此端点提供有关经过身份验证的用户配置文件的信息。要向AuthenticatedUserAPI发出请求,你可以将你的GitHub的用户名和密码以元组传递给get():

>>> from getpass import getpass

>>> requests.get( https://api.github.com/user , auth=( username , getpass()))

如果你在元组中传递给auth的凭据有效,则请求成功。如果你尝试在没有凭据的情况下发出此请求,你将看到状态代码为401Unauthorized:

>>> requests.get( https://api.github.com/user )

当你以元组形式吧用户名和密码传递给auth参数时,rqeuests将使用HTTP的基本访问认证方案来应用凭据。

因此,你可以通过使用HTTPBasicAuth传递显式的基本身份验证凭据来发出相同的请求:

>>> from requests.auth import HTTPBasicAuth

>>> from getpass import getpass

>>> requests.get(

... https://api.github.com/user ,

... auth=HTTPBasicAuth( username , getpass())

... )

虽然你不需要明确进行基本身份验证,但你可能希望使用其他方法进行身份验证。requests提供了开箱即用的其他身份验证方法,例如HTTPDigestAuth和HTTPProxyAuth。

你甚至可以提供自己的身份验证机制。为此,你必须首先创建AuthBase的子类。然后,实现__call __():

import requests

from requests.auth import AuthBase

class TokenAuth(AuthBase):

"""Implements a custom authentication scheme."""

def __init__(self, token):

self.token = token

def __call__(self, r):

"""Attach an API token to a custom auth header."""

r.headers[ X-TokenAuth ] = f {self.token} # Python 3.6+

return r

requests.get( https://httpbin.org/get , auth=TokenAuth( 12345abcde-token ))

在这里,你自定义的TokenAuth接收一个令牌,然后在你的请求头中的X-TokenAuth头中包含该令牌。

错误的身份验证机制可能会导致安全漏洞,因此,除非服务因某种原因需要自定义身份验证机制,否则你始终希望使用像Basic或OAuth这样经过验证的身份验证方案。

在考虑安全性时,让我们考虑使用requests处理SSL证书。

SSL证书验证

每当你尝试发送或接收的数据都很敏感时,安全性就很重要。通过HTTP与站点安全通信的方式是使用SSL建立加密连接,这意味着验证目标服务器的SSL证书至关重要。

好消息是requests默认为你执行此操作。但是,在某些情况下,你可能希望更改此行为。

如果要禁用SSL证书验证,请将False传递给请求函数的verify参数:

>>> requests.get( https://api.github.com , verify=False)

InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

InsecureRequestWarning)

当你提出不安全的请求时,requests甚至会发出警告来帮助你保护数据安全。

性能

在使用requests时,尤其是在生产应用程序环境中,考虑性能影响非常重要。超时控制,会话和重试限制等功能可以帮助你保持应用程序平稳运行。

超时控制

当你向外部服务发出请求时,系统将需要等待响应才能继续。如果你的应用程序等待响应的时间太长,则可能会阻塞对你的服务的请求,你的用户体验可能会受到影响,或者你的后台作业可能会挂起。

默认情况下,requests将无限期地等待响应,因此你几乎应始终指定超时时间以防止这些事情发生。要设置请求的超时,请使用timeout参数。timeout可以是一个整数或浮点数,表示在超时之前等待响应的秒数:

>>> requests.get( https://api.github.com , timeout=1)

>>> requests.get( https://api.github.com , timeout=3.05)

在第一个请求中,请求将在1秒后超时。在第二个请求中,请求将在3.05秒后超时。

你还可以将元组传递给timeout,第一个元素是连接超时(它允许客户端与服务器建立连接的时间),第二个元素是读取超时(一旦你的客户已建立连接而等待响应的时间):

>>> requests.get( https://api.github.com , timeout=(2, 5))

如果请求在2秒内建立连接并在建立连接的5秒内接收数据,则响应将按原样返回。如果请求超时,则该函数将抛出Timeout异常:

import requests

from requests.exceptions import Timeout

try:

response = requests.get( https://api.github.com , timeout=1)

except Timeout:

print( The request timed out )

else:

print( The request did not time out )

你的程序可以捕获Timeout异常并做出相应的响应。

Session对象

到目前为止,你一直在处理高级请求API,例如get()和post()。这些函数是你发出请求时所发生的事情的抽象。为了你不必担心它们,它们隐藏了实现细节,例如如何管理连接。

在这些抽象之下是一个名为Session的类。如果你需要微调对请求的控制方式或提高请求的性能,则可能需要直接使用Session实例。

Session用于跨请求保留参数。例如,如果要跨多个请求使用相同的身份验证,则可以使用session:

import requests

from getpass import getpass

# By using a context manager, you can ensure the resources used by

# the session will be released after use

with requests.Session() as session:

session.auth = ( username , getpass())

# Instead of requests.get(), you ll use session.get()

response = session.get( https://api.github.com/user )

# You can inspect the response just like you did before

print(response.headers)

print(response.json())

每次使用session发出请求时,一旦使用身份验证凭据初始化,凭据将被保留。

session的主要性能优化以持久连接的形式出现。当你的应用程序使用Session建立与服务器的连接时,它会在连接池中保持该连接。当你的应用程序想要再次连接到同一服务器时,它将重用池中的连接而不是建立新连接。

最大重试

请求失败时,你可能希望应用程序重试相同的请求。但是,默认情况下,requests不会为你执行此操作。要应用此功能,您需要实现自定义 Transport Adapter。

通过TransportAdapters,你可以为每个与之交互的服务定义一组配置。例如,假设你希望所有对于https://api.github.com的请求在最终抛出Connecti之前重试三次。你将构建一个TransportAdapter,设置其max_retries参数,并将其装载到现有的Session:

import requests

from requests.adapters import HTTPAdapter

from requests.exceptions import Connecti

github_adapter = HTTPAdapter(max_retries=3)

session = requests.Session()

# Use `github_adapter` for all requests to endpoints that start with this URL

session.mount( https://api.github.com , github_adapter)

try:

session.get( https://api.github.com )

except Connecti as ce:

print(ce)

当您将HTTPAdapter(github_adapter)挂载到session时,session将遵循其对https://api.github.com的每个请求的配置。

Timeouts,TransportAdapters和Sessions用于保持代码高效和应用程序的鲁棒性。

总结

在学习Python中强大的requests库方面,你已经走了很长的路。

你现在能够:

使用各种不同的HTTP方法发出请求,例如GET,POST和PUT

通过修改请求头,身份验证,查询字符串和消息体来自定义你的请求

检查发送到服务器的数据以及服务器发回给你的数据

使用SSL证书验证

高效的使用 requests 通过使用 max_retries, timeout, Sessions 和 TransportAdapters

因为您学会了如何使用requests,所以你可以使用他们提供的迷人数据探索广泛的Web服务世界并构建出色的应用程序了。

 类似资料: