requests vs aiohttp vs httpx: Python HTTP 클라이언트 라이브러리 엇텋게 팔서
Daniel Hayes
Full-Stack Engineer · Leapcell

Python HTTP 클라이언트 라이브러리 평가: requests, aiohttp 및 httpx
다양한 Python HTTP 클라이언트 라이브러리 중에서 가장 잘 알려진 것은 requests, aiohttp 및 httpx입니다. 다른 타사 라이브러리의 도움 없이 requests는 동기 요청만 보낼 수 있고, aiohttp는 비동기 요청만 보낼 수 있으며, httpx는 동기 및 비동기 요청을 모두 보낼 수 있습니다.
동기 및 비동기 요청의 개념
- 동기 요청: 단일 프로세스 및 단일 스레드에서 실행되는 코드에서 요청을 시작한 후 반환 결과를 받을 때까지 다음 요청을 시작할 수 없습니다.
- 비동기 요청: 단일 프로세스 및 단일 스레드에서 실행되는 코드에서 요청을 시작한 후 웹사이트가 결과를 반환할 때까지 기다리는 동안 더 많은 요청을 보낼 수 있습니다.
얕은 평가: 여러 GET 요청 전송 성능 비교
테스트 결과는 네트워크 속도와 관련이 있지만 동일한 시간대와 동일한 네트워크에서 테스트하면 이러한 라이브러리 간의 성능 차이를 확인할 수 있습니다.
requests를 사용하여 요청 보내기
import requests url = 'https://www.leapcell.io/' 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를 사용하여 요청 보내기
동기 요청
import httpx url = 'https://www.leapcell.io/' 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
로 바꾸기만 하면 코드가 정상적으로 실행됩니다.
비동기 요청
import httpx import asyncio url = 'https://www.leapcell.io/' 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의 코드는 httpx의 비동기 모드 코드와 약 90%의 코드 중복률을 갖습니다. AsyncClient
를 ClientSession
으로 바꾸기만 하면 됩니다.
성능 테스트: 100개 요청 전송 시간
requests
연결을 유지하지 않음
import time import requests url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': main()
100개 요청 전송 시간: 10.295854091644287
연결 유지
import time import requests session = requests.session() url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': main()
100개 요청 전송 시간: 4.679062128067017, 이는 분명히 약 6초 더 빠릅니다.
httpx
동기 모드
import time import httpx url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': main()
100개 요청 전송 시간: 16.60569405555725
비동기 모드: httpx.AsyncClient()를 한 번만 생성
import httpx import asyncio import time url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': asyncio.run(main())
100개 요청 전송 시간: 4.359861135482788
비동기 모드: httpx.AsyncClient()를 매번 생성
import httpx import asyncio import time url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': asyncio.run(main())
100개 요청 전송 시간: 6.378381013870239
aiohttp
aiohttp.ClientSession()을 한 번만 생성
import time import asyncio import aiohttp url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': asyncio.run(main())
100개 요청 전송 시간: 2.235464334487915
aiohttp.ClientSession()을 매번 생성
import time import asyncio import aiohttp url = 'https://www.leapcell.io/' 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'sent 100 requests, cost:{end - start}') if __name__ == '__main__': main()
100개 요청 전송 시간: 2.6662471294403076
100개 요청에 대한 속도 순위
aiohttp (클라이언트 한 번만 생성) > aiohttp (클라이언트 매번 생성) > httpx 비동기 (클라이언트 한 번만 생성) > requests.session > httpx 비동기 (클라이언트 매번 생성) > requests
결론
- 적은 수의 요청: 요청을 몇 개만 보내려면 requests 또는 httpx의 동기 모드를 사용하는 것이 코드를 가장 간단하게 만듭니다.
- requests의 연결 관리: requests가 세션을 생성하여 연결을 유지하는지 여부는 속도에 큰 차이를 만듭니다. 안티 크롤링 대책이 없는 경우 속도만 추구한다면
requests.session()
을 사용하는 것이 좋습니다. - 혼합 요청 요구 사항: 많은 요청을 보내야 하고 일부는 동기 요청이 필요하고 일부는 비동기 요청이 필요한 경우 httpx를 사용하는 것이 가장 편리합니다.
- 고속 요청 요구 사항: 많은 요청을 보내야 하고 가장 빠른 속도를 추구하는 경우 aiohttp를 사용하는 것이 가장 좋습니다.
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 Python 서비스를 배포하는 데 가장 적합한 플랫폼인 **Leapcell**을 추천하고 싶습니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 간편하게 개발하십시오.
🌍 무제한 프로젝트를 무료로 배포
사용한 만큼만 지불하십시오. 요청도 없고 요금도 없습니다.
⚡ 사용한 만큼 지불, 숨겨진 비용 없음
유휴 비용이 없고 원활한 확장성만 있습니다.
🔹 Twitter에서 팔로우하세요: @LeapcellHQ