快速入门
首先,导入 HTTPX 库:
>>> import httpx
现在,让我们尝试获取一个网页:
>>> r = httpx.get('https://httpbin.org/get')
>>> r
<Response [200 OK]>
类似地,发起一个 HTTP POST 请求:
>>> r = httpx.post('https://httpbin.org/post', data={'key': 'value'})
PUT、DELETE、HEAD 和 OPTIONS 请求都遵循相同的风格:
>>> r = httpx.put('https://httpbin.org/put', data={'key': 'value'})
>>> r = httpx.delete('https://httpbin.org/delete')
>>> r = httpx.head('https://httpbin.org/get')
>>> r = httpx.options('https://httpbin.org/get')
在 URL 中传递参数
要在请求中包含 URL 查询参数,可以使用 params
关键字:
>>> params = {'key1': 'value1', 'key2': 'value2'}
>>> r = httpx.get('https://httpbin.org/get', params=params)
要查看这些值如何编码到 URL 字符串中,我们可以检查用于发起请求的最终 URL:
>>> r.url
URL('https://httpbin.org/get?key2=value2&key1=value1')
你也可以传递一个列表作为值:
>>> params = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = httpx.get('https://httpbin.org/get', params=params)
>>> r.url
URL('https://httpbin.org/get?key1=value1&key2=value2&key2=value3')
响应内容
HTTPX 会自动处理响应内容的 Unicode 文本解码。
>>> r = httpx.get('https://www.example.org/')
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
您可以查看将用于解码响应的编码方式。
>>> r.encoding
'UTF-8'
某些情况下响应可能不包含显式编码,此时 HTTPX 会尝试自动确定要使用的编码。
>>> r.encoding
None
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
如果需要覆盖标准行为并显式设置要使用的编码,也可以这样做。
>>> r.encoding = 'ISO-8859-1'
二进制响应内容
对于非文本响应,也可以以字节形式访问响应内容:
>>> r.content
b'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
任何 gzip
和 deflate
HTTP 响应编码都会自动为您解码。如果安装了 brotlipy
,则支持 brotli
响应编码。如果安装了 zstandard
,则也支持 zstd
响应编码。
例如,要从请求返回的二进制数据创建图像,可以使用以下代码:
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
JSON 响应内容
Web API 响应通常以 JSON 编码。
>>> r = httpx.get('https://api.github.com/events')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...' ... }}]
自定义请求头
要在发出的请求中包含额外的请求头,使用 headers
关键字参数:
>>> url = 'https://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = httpx.get(url, headers=headers)
发送表单编码数据
某些类型的 HTTP 请求(如 POST
和 PUT
请求)可以在请求体中包含数据。一种常见的方式是使用表单编码数据,这种方式通常用于 HTML 表单。
>>> data = {'key1': 'value1', 'key2': 'value2'}
>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
表单编码数据还可以包含同一个键的多个值。
>>> data = {'key1': ['value1', 'value2']}
>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
...
"form": {
"key1": [
"value1",
"value2"
]
},
...
}
发送多部分文件上传
你也可以使用 HTTP 多部分编码上传文件:
>>> with open('report.xls', 'rb') as report_file:
... files = {'upload-file': report_file}
... r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
"files": {
"upload-file": "<... 二进制内容 ...>"
},
...
}
你还可以通过使用文件值的元组来显式设置文件名和内容类型:
>>> with open('report.xls', 'rb') report_file:
... files = {'upload-file': ('report.xls', report_file, 'application/vnd.ms-excel')}
... r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
"files": {
"upload-file": "<... 二进制内容 ...>"
},
...
}
如果需要在多部分表单中包含非文件数据字段,可以使用 data=...
参数:
>>> data = {'message': 'Hello, world!'}
>>> with open('report.xls', 'rb') as report_file:
... files = {'file': report_file}
... r = httpx.post("https://httpbin.org/post", data=data, files=files)
>>> print(r.text)
{
...
"files": {
"file": "<... 二进制内容 ...>"
},
"form": {
"message": "Hello, world!",
},
...
}
发送 JSON 编码数据
如果只需要简单的键值数据结构,表单编码数据就足够了。但对于更复杂的数据结构,通常需要使用 JSON 编码。
>>> data = {'integer': 123, 'boolean': True, 'list': ['a', 'b', 'c']}
>>> r = httpx.post("https://httpbin.org/post", json=data)
>>> print(r.text)
{
...
"json": {
"boolean": true,
"integer": 123,
"list": [
"a",
"b",
"c"
]
},
...
}
发送二进制请求数据
对于其他编码格式,应该使用 content=...
参数,传入 bytes
类型或生成 bytes
的生成器。
>>> content = b'Hello, world'
>>> r = httpx.post("https://httpbin.org/post", content=content)
上传二进制数据时,可能还需要设置自定义的 Content-Type
头部。
响应状态码
我们可以查看响应的 HTTP 状态码:
>>> r = httpx.get('https://httpbin.org/get')
>>> r.status_code
200
HTTPX 还提供了一个便捷方式来通过文本短语访问状态码:
>>> r.status_code == httpx.codes.OK
True
对于任何非 2xx 的成功状态码,我们可以抛出异常:
>>> not_found = httpx.get('https://httpbin.org/status/404')
>>> not_found.status_code
404
>>> not_found.raise_for_status()
Traceback (most recent call last):
File "/Users/tomchristie/GitHub/encode/httpcore/httpx/models.py", line 837, in raise_for_status
raise HTTPStatusError(message, response=self)
httpx._exceptions.HTTPStatusError: 404 Client Error: Not Found for url: https://httpbin.org/status/404
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
任何成功的响应状态码都会返回 Response
实例而不会抛出异常。
>>> r.raise_for_status()
该方法会返回响应实例,允许你进行链式调用。例如:
>>> r = httpx.get('...').raise_for_status()
>>> data = httpx.get('...').raise_for_status().json()
响应头信息
响应头信息以类似字典的接口形式提供。
>>> r.headers
Headers({
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json'
})
Headers
数据类型不区分大小写,因此可以使用任意大小写形式。
>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
根据 RFC 7230 规范,单个响应头的多个值会以逗号分隔的单一值形式表示:
接收方可以将具有相同字段名的多个头字段合并为一个"字段名: 字段值"对,通过按顺序将后续字段值追加到合并后的字段值中,用逗号分隔,且不改变消息的语义。
流式响应
对于大型下载任务,您可能需要使用流式响应,这样不会一次性将整个响应体加载到内存中。
您可以流式获取响应的二进制内容...
>>> with httpx.stream("GET", "https://www.example.com") as r:
... for data in r.iter_bytes():
... print(data)
或者流式获取响应的文本内容...
>>> with httpx.stream("GET", "https://www.example.com") as r:
... for text in r.iter_text():
... print(text)
也可以逐行流式获取文本...
>>> with httpx.stream("GET", "https://www.example.com") as r:
... for line in r.iter_lines():
... print(line)
HTTPX 会使用通用换行符,将所有情况统一标准化为 \n
。
在某些情况下,您可能希望访问响应的原始字节而不应用任何 HTTP 内容解码。这种情况下,Web 服务器应用的内容编码(如 gzip
、deflate
、brotli
或 zstd
)将不会被自动解码。
>>> with httpx.stream("GET", "https://www.example.com") as r:
... for chunk in r.iter_raw():
... print(chunk)
如果您使用上述任何一种流式响应方式,那么 response.content
和 response.text
属性将不可用,访问时会引发错误。不过,您也可以使用响应流功能来有条件地加载响应体:
>>> with httpx.stream("GET", "https://www.example.com") as r:
... if int(r.headers['Content-Length']) < TOO_LONG:
... r.read()
... print(r.text)
Cookies
响应中设置的任何 cookie 都可以轻松访问:
>>> r = httpx.get('https://httpbin.org/cookies/set?chocolate=chip')
>>> r.cookies['chocolate']
'chip'
要在发出的请求中包含 cookies,使用 cookies
参数:
>>> cookies = {"peanut": "butter"}
>>> r = httpx.get('https://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'peanut': 'butter'}}
Cookies 以 Cookies
实例的形式返回,这是一个类似字典的数据结构,提供额外的 API 用于按域名或路径访问 cookies。
>>> cookies = httpx.Cookies()
>>> cookies.set('cookie_on_domain', 'hello, there!', domain='httpbin.org')
>>> cookies.set('cookie_off_domain', 'nope.', domain='example.org')
>>> r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'cookie_on_domain': 'hello, there!'}}
重定向与历史记录
默认情况下,HTTPX 不会对所有 HTTP 方法自动跟随重定向,但可以显式启用此功能。
例如,GitHub 会将所有 HTTP 请求重定向到 HTTPS。
>>> r = httpx.get('http://github.com/')
>>> r.status_code
301
>>> r.history
[]
>>> r.next_request
<Request('GET', 'https://github.com/')>
你可以通过 follow_redirects
参数修改默认的重定向处理行为:
>>> r = httpx.get('http://github.com/', follow_redirects=True)
>>> r.url
URL('https://github.com/')
>>> r.status_code
200
>>> r.history
[<Response [301 Moved Permanently]>]
响应的 history
属性可用于检查所有跟随的重定向。它包含一个按发生顺序排列的已跟随重定向响应列表。
超时设置
HTTPX 默认对所有网络操作设置了合理的超时时间,这意味着如果连接未能正确建立,它总会抛出错误而非无限期挂起。
网络无响应的默认超时时间为 5 秒。您可以根据需要调整该值:
>>> httpx.get('https://github.com/', timeout=0.001)
也可以完全禁用超时行为...
>>> httpx.get('https://github.com/', timeout=None)
关于高级超时管理,请参阅超时精细调优。
认证机制
HTTPX 支持 Basic 和 Digest HTTP 认证方式。
要提供 Basic 认证凭证,只需将明文的 str
或 bytes
类型二元组作为 auth
参数传递给请求函数:
>>> httpx.get("https://example.com", auth=("my_user", "password123"))
如需提供 Digest 认证凭证,需要先用明文的用户名和密码实例化 DigestAuth
对象,然后将该对象作为 auth
参数传递给请求方法:
>>> auth = httpx.DigestAuth("my_user", "password123")
>>> httpx.get("https://example.com", auth=auth)
<Response [200 OK]>
异常处理
当发生错误时,HTTPX 会抛出异常。
HTTPX 中最重要的异常类是 RequestError
和 HTTPStatusError
。
RequestError
类是一个超类,包含在发起 HTTP 请求时发生的任何异常。这些异常都包含一个 .request
属性。
try:
response = httpx.get("https://www.example.com/")
except httpx.RequestError as exc:
print(f"An error occurred while requesting {exc.request.url!r}.")
HTTPStatusError
类由 response.raise_for_status()
在响应不是 2xx 成功状态码时抛出。这些异常同时包含 .request
和 .response
属性。
response = httpx.get("https://www.example.com/")
try:
response.raise_for_status()
except httpx.HTTPStatusError as exc:
print(f"Error response {exc.response.status_code} while requesting {exc.request.url!r}.")
还有一个基类 HTTPError
包含上述两类异常,可以用来捕获失败的请求或 4xx 和 5xx 响应。
你可以使用这个基类来同时捕获两类异常...
try:
response = httpx.get("https://www.example.com/")
response.raise_for_status()
except httpx.HTTPError as exc:
print(f"Error while requesting {exc.request.url!r}.")
或者显式处理每种情况...
try:
response = httpx.get("https://www.example.com/")
response.raise_for_status()
except httpx.RequestError as exc:
print(f"An error occurred while requesting {exc.request.url!r}.")
except httpx.HTTPStatusError as exc:
print(f"Error response {exc.response.status_code} while requesting {exc.request.url!r}.")
完整的异常列表,请参阅 异常 (API 参考)。