Python 异步网络:aiohttp 与异步爬虫

异步网络编程能显著提升I/O密集型任务的效率。aiohttp 是基于 asyncio 的异步HTTP框架,特别适合构建高性能网络应用。以下是核心概念与实践指南:


一、aiohttp 基础
  1. 核心组件

    • 客户端:发送异步HTTP请求
    • 服务端:构建异步Web服务
    • 关键对象:ClientSession 管理连接池
  2. 基本请求模板

import aiohttp
import asyncio

async def fetch(url):
    async with aiohttp.ClientSession() as session:  # 自动管理会话
        async with session.get(url) as response:
            return await response.text()

async def main():
    html = await fetch("https://example.com")
    print(html[:200])  # 输出前200字符

asyncio.run(main())


二、异步爬虫设计要点
  1. 并发控制
    使用信号量限制并发数:
semaphore = asyncio.Semaphore(10)  # 最大10并发

async def safe_fetch(url):
    async with semaphore:  # 限制并发
        return await fetch(url)

  1. 任务调度
    批量创建任务并收集结果:
urls = [f"https://example.com/page/{i}" for i in range(1,50)]
tasks = [safe_fetch(url) for url in urls]
results = await asyncio.gather(*tasks)  # 并行执行

  1. 错误处理
    添加重试机制:
async def robust_fetch(url, retries=3):
    for _ in range(retries):
        try:
            return await safe_fetch(url)
        except (aiohttp.ClientError, asyncio.TimeoutError):
            await asyncio.sleep(1)  # 指数退避更佳
    return None


三、性能优化策略
  1. 连接复用

    • 保持 ClientSession 单例
    • 设置连接池大小:
      connector = aiohttp.TCPConnector(limit=100)  # 最大连接数
      async with aiohttp.ClientSession(connector=connector) as session:
      

  2. 超时控制
    避免阻塞:

timeout = aiohttp.ClientTimeout(total=10)  # 10秒总超时
async with session.get(url, timeout=timeout) as response:

  1. 数据解析
    结合异步解析库:
    • aiofiles:异步文件操作
    • BeautifulSoup:在异步上下文中使用同步解析

四、实战案例:异步爬虫
import aiohttp
import asyncio
from bs4 import BeautifulSoup

async def scrape_page(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            html = await resp.text()
            soup = BeautifulSoup(html, 'lxml')
            title = soup.title.text
            return {"url": url, "title": title}

async def main():
    urls = [...]  # 目标URL列表
    tasks = [scrape_page(url) for url in urls]
    results = await asyncio.gather(*tasks)
    
    for res in results:
        print(f"URL: {res['url']}\nTitle: {res['title']}\n")

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


五、注意事项
  1. 遵守robots.txt
    使用 aiohttp_robots 包检查爬取权限
  2. 频率控制
    添加随机延迟:
    await asyncio.sleep(random.uniform(0.5, 2.0))  # 随机延时
    

  3. 资源释放
    显式关闭未完成的响应:
    if not response.closed:
        await response.release()
    

异步爬虫相较同步方案速度可提升 $n$ 倍($n$ 为并发数),但需注意:

  • 目标服务器承受能力
  • 避免触发反爬机制
  • 分布式扩展时使用 Redis + Celery 架构
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐