一、为什么用协程 + asyncio 模拟 HTTP 请求?

  • 高并发:协程+asyncio可以在单线程内高效地并发发起成百上千个HTTP请求,极大提升IO密集型任务的效率。
  • 非阻塞:不会像多线程那样有线程切换和锁的开销,也不会像同步代码那样阻塞等待响应。
  • 易于编写和维护:用同步风格(async/await)写异步代码,逻辑清晰。

二、常用库

  • Python标准库的asyncio只提供了事件循环和基础IO,不直接支持HTTP协议
  • 推荐用aiohttp库,它是基于asyncio的异步HTTP客户端/服务器库。

三、完整示例代码

1. 安装aiohttp

pip install aiohttp

2. 代码实现

import asyncio
import aiohttp

async def fetch(session, url):
    print(f'Start fetching: {url}')
    async with session.get(url) as response:
        html = await response.text()
        print(f'Finished fetching: {url}, length: {len(html)}')
        return html

async def main():
    urls = [
        'https://www.example.com',
        'https://www.python.org',
        'https://www.baidu.com',
        # 可以添加更多URL
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        print('All requests done!')

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

四、逐行详细解释

1. 导入模块

import asyncio
import aiohttp
  • asyncio:Python标准库,提供事件循环和协程支持。
  • aiohttp:第三方库,提供异步HTTP客户端。

2. 定义异步请求函数

async def fetch(session, url):
    print(f'Start fetching: {url}')
    async with session.get(url) as response:
        html = await response.text()
        print(f'Finished fetching: {url}, length: {len(html)}')
        return html
  • async def fetch(session, url):定义一个协程函数,参数为aiohttp的会话对象和目标URL。
  • async with session.get(url) as response:异步发起HTTP GET请求,获取响应对象。
  • html = await response.text():异步读取响应内容(字符串)。
  • 打印请求开始和结束信息,返回网页内容。

3. 定义主协程

async def main():
    urls = [
        'https://www.example.com',
        'https://www.python.org',
        'https://www.baidu.com',
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        print('All requests done!')
  • urls:待请求的URL列表。
  • async with aiohttp.ClientSession() as session:创建一个异步HTTP会话,自动管理连接池和资源。
  • tasks = [fetch(session, url) for url in urls]:为每个URL创建一个fetch协程任务。
  • results = await asyncio.gather(*tasks):并发调度所有任务,等待全部完成,返回结果列表。
  • 打印所有请求完成。

4. 启动事件循环

if __name__ == '__main__':
    asyncio.run(main())
  • 启动事件循环,调度并执行主协程main(),直到其执行完毕。

五、执行流程

  1. 程序启动,进入事件循环,执行main()
  2. 创建HTTP会话,生成所有fetch任务。
  3. 并发发起所有HTTP请求,每个请求遇到IO等待时自动让出控制权。
  4. 所有请求完成后,输出结果。

六、输出示例

Start fetching: https://www.example.com
Start fetching: https://www.python.org
Start fetching: https://www.baidu.com
Finished fetching: https://www.example.com, length: 1256
Finished fetching: https://www.python.org, length: 48876
Finished fetching: https://www.baidu.com, length: 2381
All requests done!

(实际输出顺序可能因网络延迟不同而不同)


七、核心原理总结

  • 协程+asyncio让你用同步风格写高并发异步HTTP请求。
  • aiohttp提供了异步HTTP客户端,支持连接池、会话复用等高效特性。
  • 适合爬虫、批量数据采集、接口压力测试等IO密集型场景。
Logo

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

更多推荐