1. 同步 vs 异步
同步爬虫按顺序请求,耗时累加。异步爬虫在等待网络响应时切换任务,大幅提升吞吐量。
2. 协程基础
python
复制
下载
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) print("World") asyncio.run(hello())async def定义协程,await挂起当前协程,让出事件循环。
3. 事件循环
事件循环是 asyncio 的核心,负责调度协程、处理 I/O 和回调。asyncio.run()会创建并关闭事件循环。
4. Task 与并发
python
复制
下载
async def fetch(i): await asyncio.sleep(1) return i async def main(): tasks = [asyncio.create_task(fetch(i)) for i in range(5)] results = await asyncio.gather(*tasks) print(results) asyncio.run(main())
gather并发执行多个任务并收集结果。
5. Future
Future 是低层对象,表示未来结果。通常使用 Task 即可,不需要手动创建 Future。
6. 超时与取消
python
复制
下载
try: await asyncio.wait_for(fetch(1), timeout=0.5) except asyncio.TimeoutError: print("超时")任务取消:
python
复制
下载
task = asyncio.create_task(fetch(1)) task.cancel()
7. 信号量控制并发
python
复制
下载
sem = asyncio.Semaphore(10) async def limited_fetch(url): async with sem: return await fetch(url)
避免一次性发起过多请求。
8. 异步 HTTP 请求
使用aiohttp或httpx:
python
复制
下载
import aiohttp async def fetch_url(session, url): async with session.get(url) as resp: return await resp.text() async def main(): async with aiohttp.ClientSession() as session: tasks = [fetch_url(session, f"https://example.com/{i}") for i in range(100)] results = await asyncio.gather(*tasks)9. 异步爬虫完整示例
python
复制
下载
import asyncio import aiohttp async def crawl(url, session, sem): async with sem: try: async with session.get(url, timeout=10) as resp: return url, resp.status, len(await resp.text()) except Exception as e: return url, "error", str(e) async def main(urls): sem = asyncio.Semaphore(20) async with aiohttp.ClientSession() as session: tasks = [crawl(url, session, sem) for url in urls] for result in asyncio.as_completed(tasks): print(await result) urls = [f"https://httpbin.org/delay/1?i={i}" for i in range(50)] asyncio.run(main(urls))10. 常见坑
在协程中调用
requests会阻塞事件循环。忘记
await导致协程未执行。异常未捕获会导致任务静默失败。
大量任务需要限流,否则可能压垮目标站点。
11. 总结
asyncio 适合 I/O 密集型场景。掌握协程、Task、Semaphore 和 aiohttp 后,可以写出高并发、低资源占用的 Python 程序。