本文目录导读:

我来为你介绍Python异步请求的几种常见方式,并给出实际案例。
基本异步请求 (使用 aiohttp)
import asyncio
import aiohttp
import time
async def fetch_url(session, url):
"""异步获取URL内容"""
try:
async with session.get(url) as response:
# 等待响应完成
content = await response.text()
return {
'url': url,
'status': response.status,
'length': len(content)
}
except Exception as e:
return {
'url': url,
'error': str(e)
}
async def main():
"""主异步函数"""
urls = [
'http://httpbin.org/delay/1',
'http://httpbin.org/delay/2',
'http://httpbin.org/delay/3',
'http://httpbin.org/get',
'http://httpbin.org/ip'
]
# 创建连接会话
async with aiohttp.ClientSession() as session:
# 创建所有任务
tasks = [fetch_url(session, url) for url in urls]
# 并发执行所有任务
results = await asyncio.gather(*tasks)
# 输出结果
for result in results:
print(f"URL: {result['url']}")
if 'error' in result:
print(f"错误: {result['error']}")
else:
print(f"状态码: {result['status']}, 内容长度: {result['length']}")
print("-" * 50)
# 运行异步函数
start_time = time.time()
asyncio.run(main())
print(f"总耗时: {time.time() - start_time:.2f}秒")
使用 httpx (支持同步和异步)
import asyncio
import httpx
import time
async def fetch_url_httpx(client, url):
"""使用httpx异步获取"""
try:
response = await client.get(url)
return {
'url': url,
'status': response.status_code,
'content': response.text[:100] # 只取前100字符
}
except Exception as e:
return {'url': url, 'error': str(e)}
async def main_httpx():
"""httpx主函数"""
urls = [
'http://httpbin.org/get',
'http://httpbin.org/ip',
'http://httpbin.org/user-agent'
]
async with httpx.AsyncClient() as client:
tasks = [fetch_url_httpx(client, url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(f"URL: {result['url']}")
if 'error' in result:
print(f"错误: {result['error']}")
else:
print(f"状态码: {result['status']}")
print("-" * 50)
start_time = time.time()
asyncio.run(main_httpx())
print(f"总耗时: {time.time() - start_time:.2f}秒")
带并发限制的异步请求
import asyncio
import aiohttp
from asyncio import Semaphore
class AsyncRequestLimiter:
"""带并发限制的异步请求器"""
def __init__(self, max_concurrent=5):
self.semaphore = Semaphore(max_concurrent)
async def fetch_with_limit(self, session, url):
"""带限制的请求"""
async with self.semaphore: # 控制并发数
return await self._fetch(session, url)
async def _fetch(self, session, url):
"""实际请求方法"""
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
return {
'url': url,
'status': response.status,
'data': await response.text()
}
except Exception as e:
return {'url': url, 'error': str(e)}
async def main_with_limit():
"""带并发限制的主函数"""
# 生成100个测试URL
urls = [f'http://httpbin.org/delay/1' for _ in range(20)]
limiter = AsyncRequestLimiter(max_concurrent=5)
async with aiohttp.ClientSession() as session:
tasks = [limiter.fetch_with_limit(session, url) for url in urls]
results = await asyncio.gather(*tasks)
# 统计结果
success = sum(1 for r in results if 'error' not in r)
failed = sum(1 for r in results if 'error' in r)
print(f"成功: {success}, 失败: {failed}")
start_time = time.time()
asyncio.run(main_with_limit())
print(f"总耗时: {time.time() - start_time:.2f}秒")
带重试机制的异步请求
import asyncio
import aiohttp
from typing import Optional
async def fetch_with_retry(
session: aiohttp.ClientSession,
url: str,
max_retries: int = 3,
retry_delay: float = 1.0
) -> Optional[dict]:
"""带重试的异步请求"""
for attempt in range(max_retries):
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as response:
if response.status == 200:
return {
'url': url,
'status': response.status,
'data': await response.json()
}
elif response.status in [429, 503]: # 限流或服务不可用
wait_time = retry_delay * (2 ** attempt) # 指数退避
print(f"请求被限流,等待 {wait_time} 秒后重试...")
await asyncio.sleep(wait_time)
else:
return {
'url': url,
'status': response.status,
'error': f"HTTP {response.status}"
}
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
if attempt < max_retries - 1:
wait_time = retry_delay * (2 ** attempt)
print(f"请求失败 ({e}),{wait_time} 秒后重试...")
await asyncio.sleep(wait_time)
else:
return {'url': url, 'error': str(e)}
return {'url': url, 'error': '达到最大重试次数'}
async def main_with_retry():
"""带重试的主函数"""
urls = [
'http://httpbin.org/status/500', # 会触发重试
'http://httpbin.org/delay/3', # 正常请求
'http://httpbin.org/get' # 正常请求
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_with_retry(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(f"URL: {result['url']}")
if 'error' in result:
print(f"最终错误: {result['error']}")
else:
print(f"状态码: {result['status']}")
print("-" * 50)
asyncio.run(main_with_retry())
实际应用:批量下载图片
import asyncio
import aiohttp
import aiofiles
import os
from pathlib import Path
class ImageDownloader:
"""图片下载器"""
def __init__(self, save_dir="downloads", max_concurrent=10):
self.save_dir = Path(save_dir)
self.save_dir.mkdir(exist_ok=True)
self.semaphore = asyncio.Semaphore(max_concurrent)
async def download_image(self, session: aiohttp.ClientSession, url: str, index: int):
"""下载单张图片"""
async with self.semaphore:
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as response:
if response.status == 200:
# 获取文件名
filename = f"image_{index}.jpg"
filepath = self.save_dir / filename
# 异步写入文件
async with aiofiles.open(filepath, 'wb') as f:
await f.write(await response.read())
return {'url': url, 'status': 'success', 'file': str(filepath)}
else:
return {'url': url, 'status': 'failed', 'error': f"HTTP {response.status}"}
except Exception as e:
return {'url': url, 'status': 'failed', 'error': str(e)}
async def download_batch(self, urls: list):
"""批量下载图片"""
async with aiohttp.ClientSession() as session:
tasks = [
self.download_image(session, url, i)
for i, url in enumerate(urls)
]
results = await asyncio.gather(*tasks)
# 统计结果
success = sum(1 for r in results if r['status'] == 'success')
failed = sum(1 for r in results if r['status'] == 'failed')
print(f"下载完成: 成功 {success} 张, 失败 {failed} 张")
return results
# 使用示例
async def download_images_example():
downloader = ImageDownloader(max_concurrent=5)
image_urls = [
"https://picsum.photos/200/300",
"https://picsum.photos/200/300",
"https://picsum.photos/200/300"
]
results = await downloader.download_batch(image_urls)
for result in results:
if result['status'] == 'success':
print(f"下载成功: {result['file']}")
else:
print(f"下载失败: {result['error']}")
# asyncio.run(download_images_example())
-
异步库选择:
aiohttp:最流行的异步HTTP客户端httpx:支持同步/异步,API更友好asyncio:Python内置异步框架
-
并发控制:
- 使用
Semaphore控制并发数 - 使用
asyncio.gather并发执行
- 使用
-
错误处理:
- 捕获
ClientError和TimeoutError - 实现重试机制和指数退避
- 捕获
-
性能优化:
- 复用
ClientSession - 使用连接池
- 合理设置超时和并发数
- 复用
这些案例涵盖了从基础到高级的异步HTTP请求用法,可以根据实际需求选择合适的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播