Skip to content

快速入门

首先,导入 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>...'

任何 gzipdeflate 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 请求(如 POSTPUT 请求)可以在请求体中包含数据。一种常见的方式是使用表单编码数据,这种方式通常用于 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 服务器应用的内容编码(如 gzipdeflatebrotlizstd)将不会被自动解码。

>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for chunk in r.iter_raw():
...         print(chunk)

如果您使用上述任何一种流式响应方式,那么 response.contentresponse.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 认证凭证,只需将明文的 strbytes 类型二元组作为 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 中最重要的异常类是 RequestErrorHTTPStatusError

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 参考)