requests、aiohttp、httpx 对比

在 Python 众多的 HTTP 客户端中,最有名的莫过于requestsaiohttphttpx。在不借助其他第三方库的情况下,requests只能发送同步请求;aiohttp只能发送异步请求;httpx既能发送同步请求,又能发送异步请求。

所谓的同步请求,是指在单进程单线程的代码中,发起一次请求后,在收到返回结果之前,不能发起下一次请求。所谓异步请求,是指在单进程单线程的代码中,发起一次请求后,在等待网站返回结果的时间里,可以继续发送更多请求。

今天我们来一个浅度测评,仅仅以多次发送 GET 请求这个角度来对比这三个库的性能。

当然测试结果与网速有关,不过在同一段时间的同一个网络测试出来,还是能看得出来问题的。

requests 发送请求

import requests

url = 'https://www.baidu.com/'
headers = headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

def main():
    res = requests.get(url,headers=headers)
    print(res.status_code)

if __name__ == '__main__':
    main()

httpx发送请求

使用 httpx 发送同步请求:

import httpx

url = 'https://www.baidu.com/'
headers = headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

def main():
    res = httpx.get(url,headers=headers)
    print(res.status_code)

if __name__ == '__main__':
      main()

httpx 的同步模式与 requests 代码重合度99%,只需要把requests改成httpx即可正常运行。

使用 httpx 发送异步请求:

import httpx
import asyncio

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}


async def main():
    async with httpx.AsyncClient() as client:
        resp = await client.get(url, headers=headers)
        print(resp.status_code)

if __name__ == '__main__':
    asyncio.run(main())

aiohttp 发送请求

import asyncio

import aiohttp

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

async def main():
    async with aiohttp.ClientSession() as client:
        async with client.get(url, headers=headers) as resp:
            print(resp.text)
            print(resp.status)

if __name__ == '__main__':
    asyncio.run(main())

aiohttp 的代码与 httpx 异步模式的代码重合度90%,只不过把AsyncClient换成了ClientSession

使用requests.post每次都会创建新的连接,速度较慢。而如果首先初始化一个 Session,那么 requests 会保持连接,从而大大提高请求速度。所以在这次测评中,我们分别对两种情况进行测试

requests

requests 不保持连接

import time
import requests

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

def make_request():
    resp = requests.get(url, headers=headers)
    print(resp.status_code)


def main():
    start = time.time()
    for _ in range(100):
        make_request()
    end = time.time()
    print(f'发送100次请求,耗时:{end - start}')

if __name__ == '__main__':
    main()

发送100次请求,耗时:10.295854091644287

requests 保持连接

import time
import requests

session = requests.session()

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

def make_request():
    resp = session.get(url, headers=headers)
    print(resp.status_code)


def main():
    start = time.time()
    for _ in range(100):
        make_request()
    end = time.time()
    print(f'发送100次请求,耗时:{end - start}')


if __name__ == '__main__':
    main()

发送100次请求,耗时:4.679062128067017,很明显快了接近 6s

httpx

httpx同步模式

import time
import httpx

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

def make_request():
    resp = httpx.get(url, headers=headers)
    print(resp.status_code)

def main():
    start = time.time()
    for _ in range(100):
        make_request()
    end = time.time()
    print(f'发送100次请求,耗时:{end - start}')


if __name__ == '__main__':
    main()

发送100次请求,耗时:16.60569405555725

httpx异步模式:只创建一次 httpx.AsyncClient()

import httpx
import asyncio
import time

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

async def make_request(client):
    resp = await client.get(url, headers=headers)
    print(resp.status_code)


async def main():
    async with httpx.AsyncClient() as client:
        start = time.time()
        tasks = [asyncio.create_task(make_request(client)) for _ in range(100)]
        await asyncio.gather(*tasks)
        end = time.time()
    print(f'发送100次请求,耗时:{end - start}')


if __name__ == '__main__':
    asyncio.run(main())

发送100次请求,耗时:4.359861135482788

httpx异步模式:每次都创建 httpx.AsyncClient()

import httpx
import asyncio
import time

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

async def make_request():
    async with httpx.AsyncClient() as client:
        resp = await client.get(url, headers=headers)
        print(resp.status_code)


async def main():
    start = time.time()
    tasks = [asyncio.create_task(make_request()) for _ in range(100)]
    await asyncio.gather(*tasks)
    end = time.time()
    print(f'发送100次请求,耗时:{end - start}')


if __name__ == '__main__':
    asyncio.run(main())

发送100次请求,耗时:6.378381013870239

aiohttp

aiohttp:只创建一次 aiohttp.ClientSession()

import time
import asyncio

import aiohttp

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

async def make_request(client):
    async with client.get(url, headers=headers) as resp:
        print(resp.status)

async def main():
    async with aiohttp.ClientSession() as client:
        start = time.time()
        tasks = [asyncio.create_task(make_request(client)) for _ in range(100)]
        await asyncio.gather(*tasks)
        end = time.time()
    print(f'发送100次请求,耗时:{end - start}')


if __name__ == '__main__':
    asyncio.run(main())

发送100次请求,耗时:2.235464334487915

aiohttp:每次都创建 aiohttp.ClientSession()

import time
import asyncio

import aiohttp

url = 'https://www.baidu.com/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}

async def make_request():
    async with aiohttp.ClientSession() as client:
        async with client.get(url, headers=headers) as resp:
            print(resp.status)

def main():
    start = time.time()
    tasks = [asyncio.ensure_future(make_request()) for _ in range(100)]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    end = time.time()
    print(f'发送100次请求,耗时:{end - start}')

if __name__ == '__main__':
    main()

发送100次请求,耗时:2.6662471294403076

请求100次速度排名

aiohttp(只创建一次client)> aiohttp(每次都创建client)> httpx异步 (只创建一次 client) > requests.session > httpx 异步(每次都创建 client) > requests > http 异步

总结

  • 如果你只发几条请求。那么使用 requests 或者 httpx 的同步模式,代码最简单。
  • requests 是否创建一个 session 保持连接,速度差别比较大,在没有反爬的情况下,只追求速度,建议用 requests.session()
  • 如果你要发送很多请求,但是有些地方要发送同步请求,有些地方要发送异步请求,那么使用 httpx 最省事。
  • 如果你要发送很多请求,并且越快越好,那么使用 aiohttp 最快。
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!