Python 异步编程入门:用 asyncio 让你的爬虫速度飞起
在网络爬虫开发中,很多人一开始用 requests 库写同步代码,遇到需要抓取几百个页面时就傻眼了——程序卡得像乌龟爬。为什么?因为同步 I/O 操作会阻塞整个线程,导致 CPU 大部分时间都在等待网络响应。
这时候,异步编程就成了救星。Python 内置的 asyncio 模块配合 aiohttp,能让你的爬虫并发处理几十甚至上百个请求,速度提升几倍到几十倍。今天我们就来一步步拆解如何用 asyncio 写一个高效的异步爬虫。
为什么需要异步?
传统同步爬虫的流程是:发送请求 → 等待响应 → 解析数据 → 下一个请求。这种“串行”方式在网络延迟高的时候效率极低。而异步编程的核心思想是“非阻塞”:当一个请求在等待服务器响应时,程序可以立即切换去处理其他请求,充分利用等待时间。
举个例子,假设你要爬取 100 个网页,每个请求平均耗时 1 秒。同步写法最少需要 100 秒,而合理使用异步并发后,可能只需 5-10 秒就能完成。
基础概念速览
- 协程 (Coroutine):用 async def 定义的函数,可以被暂停和恢复。
- 事件循环 (Event Loop):asyncio 的核心,负责调度所有协程的执行。
- Task:把协程包装成任务,让它在事件循环中并发运行。
- Await:遇到 await 关键字时,当前协程会暂停,交给事件循环去处理其他任务。
实战:异步爬虫完整示例
首先安装依赖:
pip install aiohttp aiofiles
下面是一个简单的异步爬虫模板,能并发抓取多个页面并保存为 Markdown 文件:
import asyncio
import aiohttp
from bs4 import BeautifulSoup
import aiofiles
async def fetch(url, session):
async with session.get(url) as response:
return await response.text()
async def save_to_file(filename, content):
async with aiofiles.open(filename, 'w', encoding='utf-8') as f:
await f.write(content)
async def crawl(url):
async with aiohttp.ClientSession() as session:
html = await fetch(url, session)
soup = BeautifulSoup(html, 'html.parser')
title = soup.title.string if soup.title else '无标题'
# 简单处理正文(实际项目中可根据需要提取更多内容)
content = f'# {title}\n\n{html[:500]}...'
filename = f'{title.replace(" ", "_")}.md'
await save_to_file(filename, content)
print(f'已保存:{filename}')
async def main():
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3',
# ... 添加更多 URL
]
tasks = [crawl(url) for url in urls]
await asyncio.gather(*tasks)
if __name__ == '__main__':
asyncio.run(main())
进阶技巧
- 并发控制:使用 asyncio.Semaphore 限制同时进行的请求数量,避免被目标网站封禁。
sem = asyncio.Semaphore(10) # 最多同时 10 个请求
async def fetch(url, session):
async with sem:
async with session.get(url) as resp:
return await resp.text()
2. **超时与重试**:为每个请求设置超时,并实现简单的重试机制。
3. **代理与 Headers**:在 ClientSession 中统一设置 User-Agent 和代理池,模拟不同用户行为。
4. **结合其他库**:可以和 Playwright 的异步版本配合,实现更复杂的 JavaScript 渲染页面爬取。
### 注意事项
- 遵守 robots.txt 和网站服务条款,合理控制爬取频率。
- 异步代码调试相对复杂,建议使用 logging 模块记录详细日志。
- Python 3.7+ 推荐使用 asyncio.run() 启动事件循环,更简洁安全。
掌握了 asyncio 之后,你会发现很多 I/O 密集型任务(爬虫、API 调用、文件批量处理)都能获得质的飞跃。建议大家从一个小项目开始练习,比如写一个异步下载图片或批量翻译文本的工具,很快就能感受到异步编程的魅力。
异步编程的学习曲线虽然稍陡,但一旦入门,你会爱上这种“并发思维”。欢迎在评论区分享你用 asyncio 做过的有趣项目,或者遇到的坑,我们一起交流进步!
(本文约 680 字,欢迎点赞收藏,转发给需要的朋友)