【python】从入门到精通:httpx,Python网络请求的新宠
通过对 httpx 库的深入学习,我们不难发现它在 Python 网络编程领域的独特魅力与强大实力。httpx 以其简洁一致的 API 设计,让熟悉 requests 库的开发者能够轻松上手,无缝切换 ,大大降低了学习成本。同时,它支持同步和异步两种请求方式,尤其是异步请求功能,结合 Python 的 asyncio 库,在高并发场景下展现出了卓越的性能优势,能够显著提高程序的执行效率,节省大量的
目录
一、httpx 是什么
在 Python 的网络请求库大家族中,httpx 正逐渐崭露头角,成为众多开发者的新宠。它是一个功能齐全、现代化的 HTTP 客户端库,支持同步和异步两种请求方式,为开发者在处理 HTTP 相关任务时提供了极大的便利与灵活性。
如果说 requests 库是 Python 网络请求领域的 “元老”,以简单易用、API 设计友好而被广大开发者熟知并广泛使用,那么 httpx 则像是站在巨人肩膀上的革新者 。httpx 继承了 requests 库简洁易用的优点,其 API 设计与 requests 非常相似,这使得熟悉 requests 库的开发者能够轻松上手 httpx,无缝切换。同时,httpx 又在 requests 的基础上进行了诸多功能的扩展和性能的优化,尤其是在异步编程和 HTTP/2 协议支持方面表现突出,解决了 requests 库在高并发场景下的一些局限性,为开发者打开了处理 HTTP 请求的新视野。 例如,当我们需要对多个 URL 发起请求时,使用 requests 库可能需要依次发送每个请求,在网络等待的过程中,程序会处于阻塞状态,这无疑会浪费大量的时间。而 httpx 凭借其异步特性,可以同时发送多个请求,大大提高了程序的执行效率。
二、为什么选择 httpx
(一)异步支持
在网络请求的世界里,时间就是金钱,而异步编程则是节省时间的利器,httpx 正是掌握了这一利器的高手。传统的同步请求就像是一位慢条斯理的办事员,一次只能处理一件事情,在处理 HTTP 请求时,每发送一个请求,都需要等待服务器响应后才能继续下一个请求。在等待的过程中,程序就像被按下了暂停键,其他任务都无法执行 ,这在需要处理大量请求的场景下,会极大地浪费时间和资源。
而 httpx 的异步支持就如同给程序赋予了 “分身术”。它基于 Python 的 asyncio 库,允许在等待一个请求响应的同时,去执行其他的任务,而不需要傻乎乎地干等。例如,当我们需要获取多个网页的数据时,使用同步请求,假设每个请求需要 1 秒钟的响应时间,获取 10 个网页就至少需要 10 秒钟;但使用 httpx 的异步请求,理论上只需要 1 秒钟左右(忽略网络波动和并发限制等因素),因为这些请求可以同时被发送出去,程序在等待响应的过程中可以继续做其他事情,大大提高了效率 。
我们来看一段简单的示例代码,对比同步和异步请求在并发场景下的表现:
# 同步请求示例
import requests
import time
def sync_fetch(url):
response = requests.get(url)
return response.text
urls = ['https://example.com', 'https://example2.com', 'https://example3.com']
start_time = time.time()
for url in urls:
sync_fetch(url)
end_time = time.time()
print(f"同步请求总耗时: {end_time - start_time} 秒")
# 异步请求示例
import asyncio
import httpx
async def async_fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
async def main():
urls = ['https://example.com', 'https://example2.com', 'https://example3.com']
tasks = [async_fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
return results
start_time = time.time()
asyncio.run(main())
end_time = time.time()
print(f"异步请求总耗时: {end_time - start_time} 秒")
从实际运行结果中,我们可以直观地看到异步请求在处理并发任务时,所需的时间远远少于同步请求,这就是 httpx 异步支持的魅力所在。它让程序在网络请求的等待间隙也能充分利用资源,极大地提升了程序的执行效率,特别适合在高并发场景下使用,如大规模的网页爬虫、数据采集等任务。
(二)HTTP/2 支持
在 HTTP 协议的发展历程中,HTTP/2 就像是一次重大的技术飞跃,而 httpx 正是拥抱了这一先进技术的先锋。HTTP/2 带来了诸多令人瞩目的优势,为提升网络性能立下了汗马功劳 。
首先是多路复用技术,它就像一条多车道的高速公路,允许在单一的 TCP 连接上同时进行多个请求和响应。在 HTTP/1.1 时代,浏览器针对同一域名下的请求有数量限制,超过限制的请求就会被阻塞,就像一条单车道的小路,车多了就容易堵。例如,当我们加载一个包含多个图片、脚本和样式表的网页时,使用 HTTP/1.1 可能需要建立多个 TCP 连接来分别请求这些资源,而且这些请求还得排队进行,这无疑会增加页面的加载时间。而 HTTP/2 的多路复用技术打破了这种限制,所有的请求和响应可以在同一个连接上交错进行,大大减少了延迟,提高了传输效率 。
头部压缩也是 HTTP/2 的一大亮点。HTTP 请求和响应中包含的头部信息往往占据了不少的字节数,在 HTTP/1.1 中,这些头部信息没有经过有效的压缩,每次传输都需要消耗一定的带宽。HTTP/2 引入了 HPACK 压缩算法,对头部信息进行压缩,大幅减少了头部的大小,从而降低了传输的数据量,提高了性能。想象一下,每次请求都像是寄一个包裹,包裹里除了真正有用的货物(数据),还有很多包装材料(头部信息),HTTP/2 通过优化包装材料(压缩头部),让每次 “寄包裹” 都更加高效。
服务端推送也是 HTTP/2 的一个实用功能。服务器可以主动将客户端可能需要的资源提前推送给客户端,而不需要客户端先发起请求。比如,当客户端请求一个 HTML 页面时,服务器可以预测到客户端接下来可能需要加载页面中的图片、样式表等资源,于是就提前将这些资源推送给客户端,这样当客户端解析 HTML 页面时,就能直接使用这些已经推送过来的资源,加速了页面的加载速度。
httpx 对 HTTP/2 的支持,使得开发者在使用它进行网络请求时,能够充分享受到这些优势带来的性能提升。无论是在加载网页、获取数据接口还是进行其他 HTTP 相关的操作,基于 HTTP/2 的 httpx 都能让应用程序更加快速、高效地与服务器进行通信,为用户提供更流畅的体验 。
(三)简洁一致的 API
对于广大 Python 开发者来说,学习成本是选择一个新库时不得不考虑的因素,而 httpx 在这方面表现得十分友好,它拥有简洁一致的 API,与大家熟知的 requests 库极为相似。这就好比你已经熟练掌握了一种语言(requests 库的使用),突然出现了一种新语言(httpx 库),但让你惊喜的是,这两种语言的语法和词汇大部分是相通的,学习起来自然就轻松许多。
requests 库以其简单易用的 API 而闻名,它的设计非常符合 Python 的 “优雅、明确、简单” 的理念,使得开发者能够轻松地发送 HTTP 请求、处理响应等。httpx 在设计 API 时,充分借鉴了 requests 库的优点,保持了类似的函数和方法命名,以及相似的参数设置方式。例如,使用 requests 发送一个 GET 请求的代码是:
import requests
response = requests.get('https://example.com')
print(response.status_code)
print(response.text)
而使用 httpx 发送同样的 GET 请求,代码如下:
import httpx
response = httpx.get('https://example.com')
print(response.status_code)
print(response.text)
可以看到,除了导入的库名不同,其他部分几乎一模一样。对于已经熟悉 requests 库的开发者来说,几乎可以零成本地切换到 httpx,快速上手并使用它的各种功能。这种相似性不仅体现在简单的 GET 请求上,在处理 POST 请求、设置请求头、传递参数、处理 cookies 等复杂操作时,httpx 的 API 也与 requests 保持了高度的一致性 。
这使得开发者在学习和使用 httpx 时,无需花费大量时间去重新学习一套全新的 API,能够将在 requests 库上积累的经验和知识直接应用到 httpx 中,大大提高了开发效率。同时,简洁一致的 API 也使得代码的可读性和维护性更强,无论是自己回顾代码,还是团队成员之间协作开发,都能更加顺畅地理解和修改代码 。
三、安装 httpx
在开始使用 httpx 库开启我们的高效网络请求之旅前,首先要将它成功安装到我们的开发环境中。安装 httpx 非常简单,使用 Python 的包管理工具 pip 即可轻松完成 ,而且它在不同操作系统上的安装过程都十分相似,下面就为大家详细介绍。
(一)Windows 系统安装
如果你使用的是 Windows 系统,打开命令提示符(CMD)或 PowerShell,输入以下命令进行普通安装,即可从 Python Package Index(PyPI)上下载并安装 httpx 库:
pip install httpx
如果你希望使用 httpx 的 HTTP/2 支持,需要安装额外的依赖,命令如下:
pip install httpx[http2]
安装过程中,pip 会自动处理 httpx 及其依赖项的下载和安装。等待安装完成后,你就可以在 Python 项目中愉快地使用 httpx 了。 例如,你可以打开 Python 交互式环境,输入import httpx,如果没有报错,就说明安装成功。
(二)macOS 系统安装
对于 macOS 用户,同样可以使用 pip 来安装 httpx。打开终端,执行普通安装命令:
pip3 install httpx
这里使用pip3是为了确保安装到 Python 3 的环境中,因为 macOS 系统可能同时存在 Python 2 和 Python 3。如果需要 HTTP/2 支持,执行以下命令:
pip3 install httpx[http2]
安装完成后,你可以在 Python 脚本或交互式环境中导入 httpx 进行测试,比如创建一个简单的 Python 脚本test_httpx.py,内容如下:
import httpx
response = httpx.get('https://www.apple.com')
print(response.status_code)
然后在终端中运行python3 test_httpx.py,如果能正确输出苹果官网的响应状态码,那就大功告成了 。
(三)Linux 系统安装
在 Linux 系统上,安装步骤也大致相同。打开终端,使用 pip 进行普通安装:
pip install httpx
若要安装支持 HTTP/2 的版本,输入:
pip install httpx[http2]
安装完成后,你可以通过编写 Python 代码来验证安装是否成功。比如在 Linux 系统的文本编辑器中创建一个 Python 文件,如test_httpx_linux.py,内容为:
import httpx
response = httpx.get('https://kernel.org')
print(response.status_code)
保存文件后,在终端中运行python test_httpx_linux.py,如果能获取到 Linux 内核官网的响应状态码,就说明 httpx 已经成功安装在你的 Linux 系统中了 。
四、httpx 基础用法
(一)同步请求
httpx 的同步请求方式非常简单,与我们熟悉的 requests 库极为相似,这使得熟悉 requests 库的开发者能够轻松上手。它支持多种 HTTP 请求方法,如 GET、POST、PUT、DELETE 等,每种方法都对应着对服务器资源的一种操作。下面我们以获取网页内容为例,来详细展示这些请求方法的使用。
GET 请求是最常用的请求方法之一,主要用于从服务器获取资源。比如我们要获取一个网页的内容,代码如下:
import httpx
# 发送GET请求
response = httpx.get('https://www.example.com')
# 检查响应状态码
if response.status_code == 200:
print('请求成功')
# 输出网页内容
print(response.text)
else:
print(f'请求失败,状态码: {response.status_code}')
在这段代码中,我们使用httpx.get方法发送了一个 GET 请求到Example Domain,然后通过response.status_code检查响应状态码,判断请求是否成功。如果状态码为 200,表示请求成功,我们可以通过response.text获取网页的文本内容。
POST 请求通常用于向服务器提交数据,比如提交表单数据、上传文件等。假设我们有一个用户注册的 API,需要提交用户名和密码,代码如下:
import httpx
data = {
'username': 'test_user',
'password': 'test_password'
}
# 发送POST请求
response = httpx.post('https://example.com/register', data=data)
if response.status_code == 200:
print('注册成功')
else:
print(f'注册失败,状态码: {response.status_code}')
这里我们使用httpx.post方法,将用户注册数据通过data参数传递给服务器。服务器接收到数据后,会进行相应的处理,如将用户信息保存到数据库中。
PUT 请求一般用于更新服务器上的资源。例如,我们要更新一个用户的邮箱地址,代码如下:
import httpx
update_data = {
'email': 'new_email@example.com'
}
# 发送PUT请求
response = httpx.put('https://example.com/users/123', json=update_data)
if response.status_code == 200:
print('用户邮箱更新成功')
else:
print(f'用户邮箱更新失败,状态码: {response.status_code}')
在这个例子中,我们通过httpx.put方法向https://example.com/users/123发送 PUT 请求,其中123是用户的 ID,json=update_data表示将更新数据以 JSON 格式传递给服务器,服务器会根据这个 ID 找到对应的用户,并更新其邮箱地址。
DELETE 请求用于删除服务器上的资源。比如要删除一个用户,代码如下:
import httpx
# 发送DELETE请求
response = httpx.delete('https://example.com/users/123')
if response.status_code == 200:
print('用户删除成功')
else:
print(f'用户删除失败,状态码: {response.status_code}')
这里使用httpx.delete方法向https://example.com/users/123发送 DELETE 请求,服务器会根据这个请求删除 ID 为123的用户。
(二)异步请求
httpx 的异步请求功能使其在处理高并发任务时表现出色,而这一强大功能的实现离不开 Python 的 asyncio 库。asyncio 库为 Python 提供了异步编程的基础设施,让我们能够编写高效的异步代码 。下面我们就结合 asyncio 库来演示 httpx 的异步请求实现,以及如何处理并发请求。
首先,我们来看一个简单的异步请求示例,获取一个网页的内容:
import asyncio
import httpx
async def async_fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
async def main():
url = 'https://www.example.com'
content = await async_fetch(url)
print(content)
asyncio.run(main())
在这段代码中,我们定义了一个异步函数async_fetch,它使用httpx.AsyncClient()创建一个异步客户端。async with语句用于管理客户端的生命周期,确保在请求完成后正确关闭客户端。await client.get(url)表示异步发送 GET 请求,并等待响应。这里的await关键字是异步编程的关键,它会暂停当前函数的执行,直到client.get(url)这个异步操作完成,然后返回响应结果。
main函数是我们的主异步函数,它调用async_fetch函数获取网页内容,并打印出来。最后,asyncio.run(main())用于运行整个异步任务,启动事件循环,执行异步代码。
当我们需要处理多个并发请求时,httpx 和 asyncio 的组合更是展现出了强大的威力。假设我们有多个 URL 需要获取内容,代码如下:
import asyncio
import httpx
async def async_fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
async def main():
urls = [
'https://www.example1.com',
'https://www.example2.com',
'https://www.example3.com'
]
tasks = [async_fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)
asyncio.run(main())
在这个示例中,我们首先定义了一个 URL 列表urls。然后,通过列表推导式[async_fetch(url) for url in urls]创建了多个异步任务,每个任务对应一个 URL 的获取请求。asyncio.gather(*tasks)用于并发运行这些任务,并等待所有任务完成。*tasks是将任务列表解包,作为参数传递给asyncio.gather。最后,通过遍历results列表,打印每个请求的响应结果。这样,我们就可以同时发送多个请求,大大提高了效率,避免了同步请求中依次等待每个请求响应的时间浪费,充分发挥了异步编程在高并发场景下的优势 。
五、httpx 高级用法
(一)设置请求头
在网络请求的世界里,请求头就像是我们发送请求时携带的 “身份名片”,它包含了丰富的信息,如客户端的类型、可接受的数据类型、语言偏好等 。在使用 httpx 进行网络请求时,设置请求头是一项非常重要的技能,尤其是在应对反爬虫机制时,通过设置合适的请求头,我们可以模拟不同浏览器的访问行为,从而降低被目标网站识别为爬虫的风险。
首先,我们来看如何设置请求头。在 httpx 中,设置请求头非常简单,只需要在发送请求时,通过headers参数传递一个字典即可。例如,我们要设置User - Agent请求头,模拟 Chrome 浏览器访问,代码如下:
import httpx
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'
}
response = httpx.get('https://www.example.com', headers=headers)
print(response.status_code)
在这段代码中,我们创建了一个headers字典,其中User - Agent字段的值是 Chrome 浏览器的标识。然后在发送 GET 请求时,将这个headers字典作为参数传递给httpx.get方法。这样,服务器接收到的请求就会显示是来自 Chrome 浏览器的访问。
除了User - Agent,还有很多其他常用的请求头字段,如Accept(告知服务器客户端可接受的 MIME 类型)、Accept - Language(告知服务器客户端可接受的语言)、Referer(告知服务器当前请求是从哪个页面链接过来的)等 。我们可以根据实际需求,在headers字典中添加更多的字段。例如:
import httpx
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',
'Accept - Language': 'en - US,en;q = 0.5',
'Referer': 'https://www.google.com'
}
response = httpx.get('https://www.example.com', headers=headers)
print(response.status_code)
通过设置这些请求头,我们可以更全面地模拟真实浏览器的访问行为。在实际的爬虫场景中,很多网站会通过检测请求头来判断是否为爬虫访问,如果请求头中没有包含必要的字段或者字段值不符合正常浏览器的特征,就可能会返回错误信息或者拒绝访问 。通过设置合理的请求头,我们可以绕过一些简单的反爬虫机制,提高爬虫的成功率。同时,不同的网站可能对请求头有不同的要求,我们需要根据目标网站的特点,灵活调整请求头的设置 。
(二)传递参数
在 HTTP 请求中,传递参数是与服务器进行交互的常见方式之一。httpx 提供了简洁明了的方法来帮助我们在 URL 中传递参数,无论是普通的键值对参数,还是比较特殊的列表类型参数,都能轻松应对。
对于普通参数的传递,我们可以在发送请求时,通过params参数来实现。例如,我们要向一个搜索 API 发送请求,传递关键词参数,代码如下:
import httpx
params = {
'q': 'python programming'
}
response = httpx.get('https://example.com/search', params=params)
print(response.text)
在这个例子中,我们创建了一个params字典,其中q是参数名,python programming是参数值。当我们使用httpx.get发送请求时,params字典会被自动编码并添加到 URL 的查询字符串中,最终发送的请求 URL 类似于https://example.com/search?q=python+programming 。
当需要传递列表类型的参数时,httpx 同样能够很好地处理。假设我们有一个 API,需要传递多个 ID 进行查询,代码如下:
import httpx
ids = [123, 456, 789]
params = {
'ids': ids
}
response = httpx.get('https://example.com/query', params=params)
print(response.text)
在这段代码中,ids是一个列表,我们将其作为参数值传递给params字典中的ids键。httpx 会自动将列表中的每个元素进行编码,并正确地添加到 URL 中,最终生成的 URL 可能类似于https://example.com/query?ids=123&ids=456&ids=789 。这样,服务器就能够接收到我们传递的多个 ID 参数,并进行相应的处理 。
(三)处理响应
当我们使用 httpx 发送 HTTP 请求后,服务器会返回一个响应。如何有效地处理这个响应,获取我们需要的信息,是使用 httpx 的关键环节之一。httpx 提供了丰富的属性和方法,让我们能够轻松地获取响应状态码、文本内容、二进制内容、JSON 数据等。
首先,获取响应状态码是判断请求是否成功的重要依据。在 httpx 中,通过response.status_code属性即可获取响应状态码。例如:
import httpx
response = httpx.get('https://www.example.com')
if response.status_code == 200:
print('请求成功')
else:
print(f'请求失败,状态码: {response.status_code}')
这里,我们通过判断response.status_code是否等于 200,来确定请求是否成功。如果状态码为 200,表示请求成功,服务器正常返回了数据;如果状态码不为 200,则表示请求过程中出现了问题,我们可以根据具体的状态码来分析问题所在 。
获取响应的文本内容也是非常常见的操作。通过response.text属性,我们可以直接获取服务器返回的文本数据,通常用于获取网页的 HTML 内容、纯文本文件等。例如:
import httpx
response = httpx.get('https://www.example.com')
if response.status_code == 200:
print(response.text)
这段代码会在请求成功时,打印出目标网页的 HTML 内容。
当我们请求的是二进制数据,如图像、音频、视频等文件时,就需要使用response.content属性来获取二进制内容。例如,下载一张图片:
import httpx
response = httpx.get('https://example.com/image.jpg')
if response.status_code == 200:
with open('image.jpg', 'wb') as f:
f.write(response.content)
在这个例子中,我们在请求成功后,使用open函数以二进制写入模式打开一个文件image.jpg,然后将response.content中的二进制数据写入文件,从而实现了图片的下载 。
如果服务器返回的是 JSON 格式的数据,我们可以使用response.json()方法将响应内容解析为 Python 的字典或列表。例如,请求一个返回 JSON 数据的 API:
import httpx
response = httpx.get('https://example.com/api/data')
if response.status_code == 200:
data = response.json()
print(data)
这段代码会在请求成功后,将服务器返回的 JSON 数据解析为 Python 对象并打印出来,方便我们进一步处理和分析数据 。
六、常见问题及解决方案
(一)SSL 验证失败
在使用 httpx 进行 HTTPS 请求时,有时会遇到 SSL 验证失败的情况。这通常是因为目标服务器的 SSL 证书存在问题,比如证书过期、证书不被信任的 CA 颁发等 。当遇到这种情况时,一种简单但存在风险的解决方法是禁用 SSL 验证。在 httpx 中,通过设置verify=False即可实现:
import httpx
response = httpx.get('https://example.com', verify=False)
然而,需要特别注意的是,禁用 SSL 验证会带来安全风险。SSL 证书的作用是确保通信双方的身份真实性和数据传输的加密性 。禁用验证后,程序将无法确认服务器的真实身份,这就好比你在和一个陌生人交流,但却不确认对方的真实身份,很容易遭受中间人攻击。攻击者可能会拦截、篡改或窃取传输的数据,导致信息泄露、数据被破坏等严重后果 。
为了更安全地解决 SSL 验证失败的问题,我们可以尝试以下方法:
- 更新 CA 证书:有时候 SSL 验证失败是因为本地的 CA 证书库过旧,无法识别新的或更新后的 SSL 证书。我们可以通过更新操作系统或 Python 环境中的 CA 证书来解决这个问题。例如,在 Linux 系统中,可以使用包管理工具更新 CA 证书,如apt-get update ca-certificates(Debian/Ubuntu 系)或yum update ca-certificates(CentOS/RHEL 系) 。在 Python 中,也可以使用certifi库来获取最新的 CA 证书,certifi库提供了一个广泛认可的 CA 证书集合,能够帮助我们验证 SSL 证书的有效性。在使用 httpx 时,可以将certifi库的证书路径传递给verify参数,如下所示:
import httpx
import certifi
response = httpx.get('https://example.com', verify=certifi.where())
- 使用自定义 SSL 上下文:如果目标服务器使用的是自签名证书或特殊的证书配置,我们可以创建一个自定义的 SSL 上下文,加载服务器的证书或信任列表来进行验证 。首先,我们需要导入ssl模块,创建一个默认的 SSL 上下文,并使用load_verify_locations方法加载证书文件,然后将这个上下文传递给 httpx 的verify参数。例如:
import httpx
import ssl
context = ssl.create_default_context()
context.load_verify_locations(cafile='path/to/your/cert.pem')
response = httpx.get('https://example.com', verify=context)
通过以上方法,我们可以在确保安全的前提下,解决 httpx 中的 SSL 验证失败问题,避免因禁用 SSL 验证而带来的安全隐患 。
(二)超时处理
在网络请求过程中,超时是一个常见的问题。如果服务器响应过慢或者网络出现波动,请求可能会长时间处于等待状态,这会影响程序的执行效率和用户体验。httpx 提供了灵活的超时设置方式,帮助我们有效地处理这种情况 。
在 httpx 中,我们可以通过timeout参数来设置请求的超时时间,单位为秒。例如,设置一个 5 秒的超时时间:
import httpx
response = httpx.get('https://example.com', timeout=5)
如果在 5 秒内没有收到服务器的响应,将会抛出httpx.ReadTimeout异常,我们可以捕获这个异常并进行相应的处理,比如记录日志、重试请求等 。
超时又可以细分为连接超时(connect)、读取超时(read)、写入超时(write)和池超时(pool)。如果我们需要对这些超时时间进行更详细的设置,可以通过httpx.Timeout类来实现 。例如,设置连接超时为 2 秒,读取超时为 10 秒:
import httpx
timeout = httpx.Timeout(connect=2, read=10)
response = httpx.get('https://example.com', timeout=timeout)
在实际应用中,合理调整超时参数是非常重要的。如果超时时间设置得过短,可能会导致一些正常的请求因为网络瞬间波动等原因而被判定为超时;如果设置得过长,又会使程序在等待响应时浪费过多的时间。我们需要根据网络情况和业务需求来进行权衡 。例如,对于一些对实时性要求较高的业务,如获取股票行情数据,我们可以将超时时间设置得相对较短,以保证能够及时获取最新数据;而对于一些不太在意响应时间的后台任务,如定期更新缓存数据,可以适当延长超时时间,避免因为偶尔的网络问题导致任务失败 。同时,我们还可以结合重试机制,在请求超时时自动重试一定次数,提高请求的成功率 。
(三)处理重定向
在 HTTP 协议中,重定向是一种常见的机制。当服务器返回重定向状态码(如 301、302、303、307、308 等)时,客户端会根据服务器的指示跳转到新的 URL 。在使用 httpx 进行网络请求时,默认情况下,它会自动处理 HTTP 重定向,跟随服务器的指示访问新的 URL 。例如:
import httpx
response = httpx.get('https://example.com/redirect')
print(response.url)
在这个例子中,如果https://example.com/redirect返回了重定向状态码,httpx 会自动跳转到新的 URL,并将最终的响应结果返回,response.url将显示最终访问的 URL。
然而,在某些情况下,我们可能需要手动控制重定向行为,以满足不同的场景需求 。比如,在进行网页爬虫时,我们可能希望获取重定向过程中的所有 URL,或者避免陷入无限重定向的循环中;在进行 API 调用时,可能需要根据重定向的情况进行特殊的处理 。
要手动控制重定向,我们可以通过follow_redirects参数来实现。将follow_redirects设置为False,即可阻止 httpx 自动处理重定向 。例如:
import httpx
response = httpx.get('https://example.com/redirect', follow_redirects=False)
if response.is_redirect:
print(f'重定向到: {response.headers["Location"]}')
在这段代码中,当请求的 URL 返回重定向状态码时,httpx 不会自动跳转,而是将响应返回。我们可以通过response.is_redirect判断是否为重定向响应,如果是,可以通过response.headers["Location"]获取重定向的目标 URL 。
此外,我们还可以通过max_redirects参数来限制重定向的次数,防止出现无限重定向的情况 。例如,设置最大重定向次数为 3:
import httpx
response = httpx.get('https://example.com/redirect', follow_redirects=True, max_redirects=3)
如果在重定向过程中,重定向次数超过了 3 次,将会抛出httpx.TooManyRedirects异常,我们可以捕获这个异常并进行处理,比如记录错误日志、提示用户等 。通过灵活运用这些参数,我们能够更好地控制 httpx 的重定向行为,满足各种复杂的网络请求场景需求 。
七、实战案例
(一)简单爬虫示例
现在,我们来通过一个具体的实战案例,更加深入地了解 httpx 在实际应用中的强大威力。假设我们要抓取豆瓣电影 Top250 的信息,这是一个非常经典的爬虫入门案例。豆瓣电影 Top250 页面包含了电影的名称、评分、导演、主演等丰富信息,对于电影爱好者和数据分析师来说,这些数据具有很高的价值 。
首先,我们需要分析豆瓣电影 Top250 页面的结构和请求规律。通过浏览器的开发者工具,我们可以发现,每一页展示 25 条电影信息,URL 的规律是豆瓣电影 Top 250,其中{start}是起始索引,从 0 开始,每一页递增 25 。例如,第一页的start为 0,第二页的start为 25,以此类推 。
接下来,我们使用 httpx 编写爬虫代码。在这个过程中,我们将结合正则表达式来解析网页内容,提取出我们需要的电影信息 。正则表达式是一种强大的文本匹配工具,它能够帮助我们从复杂的网页文本中精准地提取出目标数据 。
import httpx
import re
def fetch_douban_movies():
base_url = 'https://movie.douban.com/top250?start={start}&filter='
movies = []
for start in range(0, 250, 25):
url = base_url.format(start=start)
response = httpx.get(url)
if response.status_code == 200:
html = response.text
pattern = re.compile(
r'<div class="item">.*?<span class="title">(.*?)</span>.*?<span class="rating_num" property="v:average">(.*?)</span>.*?<p class="">(.*?)</p>',
re.S)
results = re.findall(pattern, html)
for result in results:
title = result[0]
rating = result[1]
info = result[2].strip().replace('\n', '').replace(' ', '')
movies.append({'title': title, 'rating': rating, 'info': info})
else:
print(f'请求失败,状态码: {response.status_code}')
return movies
movies = fetch_douban_movies()
for movie in movies:
print(movie)
在这段代码中,我们首先定义了fetch_douban_movies函数。在函数内部,我们使用for循环遍历每一页的 URL,通过httpx.get方法发送 GET 请求获取网页内容 。如果请求成功,我们使用正则表达式来解析网页内容。正则表达式r'<div class="item">.*?<span class="title">(.*?)</span>.*?<span class="rating_num" property="v:average">(.*?)</span>.*?<p class="">(.*?)</p>'是整个解析过程的核心,它的作用是匹配网页中每一部电影的信息 。其中,(.*?)是捕获组,用于提取我们需要的电影名称、评分和其他信息 。re.S是修饰符,它使得正则表达式中的.可以匹配包括换行符在内的任意字符,因为网页中的电影信息可能跨越多行 。通过re.findall方法,我们可以找到所有匹配的电影信息,并将其存储在movies列表中 。最后,我们返回movies列表,并遍历打印每一部电影的信息 。
通过这个简单的爬虫示例,我们可以看到 httpx 与正则表达式的完美配合,能够高效地从网页中抓取和解析数据,为我们获取有价值的信息提供了有力的支持 。
(二)API 数据获取
在实际的开发过程中,调用第三方 API 获取数据是一项非常常见的任务。以聚合数据的天气预报 API 为例,我们可以通过这个 API 获取全国各地的实时天气信息,这对于开发天气相关的应用、网站或者进行数据分析都非常有帮助 。
首先,我们需要在聚合数据平台上注册账号,申请天气预报 API 的使用权限,获取 API Key,这是我们调用 API 的凭证 。假设我们要获取北京的实时天气信息,API 的请求 URL 为http://v.juhe.cn/weather/index,请求方式为 GET,需要传递的参数有cityname(城市名称)和key(API Key) 。
下面是使用 httpx 调用该 API 的代码示例:
import httpx
def get_weather():
api_url = 'http://v.juhe.cn/weather/index'
params = {
'cityname': '北京',
'key': 'your_api_key'
}
response = httpx.get(api_url, params=params)
if response.status_code == 200:
data = response.json()
if data['error_code'] == 0:
weather_info = data['result']['today']
print(f"北京今日天气:{weather_info['weather']},温度:{weather_info['temperature']}")
else:
print(f"获取天气信息失败,错误码:{data['error_code']},错误信息:{data['reason']}")
else:
print(f'请求失败,状态码: {response.status_code}')
get_weather()
在这段代码中,我们定义了get_weather函数。在函数内部,我们首先设置了 API 的请求 URL 和参数,其中params字典中包含了城市名称和 API Key 。然后,使用httpx.get方法发送 GET 请求,并将参数传递给服务器 。如果请求成功,我们通过response.json()方法将响应内容解析为 JSON 格式的数据 。接着,我们检查返回数据中的error_code,如果error_code为 0,表示请求成功,我们可以从返回的数据中提取出今天的天气信息并打印 。如果error_code不为 0,说明请求过程中出现了问题,我们打印出错误码和错误信息 。如果请求失败,我们打印出响应的状态码 。
通过这个 API 数据获取的示例,我们展示了如何使用 httpx 轻松地与第三方 API 进行交互,获取我们需要的数据,并对返回的数据进行处理,这在实际的开发中具有广泛的应用场景 。
八、总结与展望
通过对 httpx 库的深入学习,我们不难发现它在 Python 网络编程领域的独特魅力与强大实力。httpx 以其简洁一致的 API 设计,让熟悉 requests 库的开发者能够轻松上手,无缝切换 ,大大降低了学习成本。同时,它支持同步和异步两种请求方式,尤其是异步请求功能,结合 Python 的 asyncio 库,在高并发场景下展现出了卓越的性能优势,能够显著提高程序的执行效率,节省大量的时间和资源 。对 HTTP/2 协议的支持更是为 httpx 锦上添花,使它能够充分利用 HTTP/2 的多路复用、头部压缩、服务端推送等特性,提升网络请求的速度和效率,为用户带来更流畅的网络体验 。
展望未来,随着网络技术的不断发展和应用场景的日益丰富,httpx 有望在更多领域发挥重要作用。在网络爬虫领域,其异步请求和高效的性能将继续助力开发者快速、稳定地获取大量网页数据,满足数据采集和分析的需求 。在微服务架构中,服务之间的通信频繁且对性能要求较高,httpx 凭借其出色的 HTTP 请求处理能力,能够成为微服务间通信的得力工具,确保服务之间的数据交互高效、可靠 。在 API 测试方面,httpx 简洁的 API 和灵活的请求设置,使其能够方便地模拟各种请求场景,对 API 进行全面、深入的测试,保障 API 的质量和稳定性 。
同时,我们也期待 httpx 在未来能够不断演进和完善。例如,进一步优化性能,提高在极端高并发场景下的稳定性和响应速度;加强对新兴网络技术和协议的支持,如 HTTP/3 等,以适应不断变化的网络环境;丰富其生态系统,提供更多的插件、中间件和工具,方便开发者根据不同的业务需求进行定制和扩展 。相信在开发者社区的共同努力下,httpx 将不断发展壮大,为 Python 网络编程带来更多的惊喜和可能,成为广大开发者在网络编程领域不可或缺的得力助手 。
更多推荐
所有评论(0)