Python异步编程实战:让代码跑得比AI还快
Python异步编程实战:让代码跑得比AI还快
大家好,我是船长。
上周有个读者问我:"船长,我爬虫跑10000个URL要3小时,有没有办法加速?"
我看了下他的代码——用的是同步requests。10000个URL,每个假设耗时1秒,再怎么优化也很难突破这个天花板。
换成异步之后,同样的任务,8分钟跑完。
这就是今天要讲的东西:Python异步编程。
一、为什么异步能快这么多?
先理解一个概念:同步 vs 异步。
同步代码,就像排队买奶茶。你站在柜台前,等服务员做好一杯,下一个人才能点单。10000个人,就要等10000次。
异步代码,就像自助点单机。你点完单就去玩手机,叫你了再来拿。10000个人同时点单,最后一起来拿。
关键点在于:等待IO的时候,CPU是空闲的。网络请求、文件读写、数据库查询——这些操作99%的时间都在等。
异步编程就是让你在等待的时候"顺便"干别的事。
二、asyncio基础:Hello World
先看最简单例子:
import asyncio async def say_hello(): print("Hello") await asyncio.sleep(1) # 模拟IO等待 print("World") # 运行 asyncio.run(say_hello())几点说明:
async def定义异步函数await暂停当前协程,让出CPUasyncio.run()入口函数
三、实战场景1:并发HTTP请求
这是最常见的需求。安装依赖:
pip install aiohttp完整代码:
import asyncio import aiohttp import time async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(urls): async with aiohttp.ClientSession() as session: tasks = [fetch(session, url) for url in urls] results = await asyncio.gather(*tasks) return results # 测试 urls = [f"https://httpbin.org/delay/1" for _ in range(100)] start = time.time() asyncio.run(main(urls)) print(f"100个请求耗时: {time.time() - start:.2f}秒")输出:100个请求耗时: 1.23秒
如果是同步requests,同样的代码要100秒。提升80倍。
四、实战场景2:批量文件读写
import asyncio import aiofiles async def read_file(path): async with aiofiles.open(path, mode='r') as f: return await f.read() async def process_files(file_paths): tasks = [read_file(path) for path in file_paths] contents = await asyncio.gather(*tasks) return contents # 使用 import time file_list = [f"data_{i}.txt" for i in range(1000)] start = time.time() contents = asyncio.run(process_files(file_list)) print(f"读取1000个文件耗时: {time.time() - start:.2f}秒")aiofiles让文件IO也能异步。
五、实战场景3:数据库批量插入
import asyncio import asyncpg async def batch_insert(records): conn = await asyncpg.connect( host='localhost', port=5432, user='user', password='password', database='dbname' ) # 批量插入,比逐条插入快10倍+ await conn.executemany( "INSERT INTO users(id, name) VALUES($1, $2)", records ) await conn.close() # 使用 records = [(i, f"user_{i}") for i in range(10000)] asyncio.run(batch_insert(records))asyncpg是PostgreSQL的异步驱动,比同步psycopg2快很多。
六、实战场景4:异步爬虫(完整示例)
import asyncio import aiohttp import aiofiles from bs4 import BeautifulSoup async def crawl_page(session, url, semaphore): async with semaphore: # 限制并发数,避免被封 try: async with session.get(url) as response: html = await response.text() soup = BeautifulSoup(html, 'html.parser') title = soup.find('title').text # 异步写入文件 async with aiofiles.open(f"output/{url.split('/')[-1]}.txt", 'w') as f: await f.write(title) return title except Exception as e: print(f"Error: {url} - {e}") return None async def main(start_url, max_pages=100): # 1. 先获取所有链接 async with aiohttp.ClientSession() as session: # 省略爬取链接的逻辑 urls = [f"https://example.com/page/{i}" for i in range(max_pages)] # 2. 并发抓取(限制同时50个请求) semaphore = asyncio.Semaphore(50) async with aiohttp.ClientSession() as session: tasks = [crawl_page(session, url, semaphore) for url in urls] results = await asyncio.gather(*tasks) return results # 运行 asyncio.run(main("https://example.com"))几个要点:
Semaphore限制并发数,太高会被封IP- 异常处理必须有,网络请求随时会失败
gather收集所有结果
七、注意事项
1. 不要混用同步和异步
requests是同步库,不能用在async函数里。要用aiohttp。
2. 异步不等于多线程
asyncio是单线程,只是切换执行权。如果CPU密集型任务(如加密、压缩),要用multiprocessing。
3. 调试比同步代码难
print可能不会按顺序输出。用logging代替。
总结
异步编程的核心场景:
- 网络请求(爬虫、API调用)
- 文件IO(批量读写)
- 数据库操作(批量插入查询)
- 消息队列消费
提升效果:IO密集型任务,10-100倍性能提升。
代码复杂度:比同步稍高,但值得。
完整代码已上传到GitHub,需要的同学公众号后台回复"异步"获取链接。
有问题欢迎评论区聊聊。
【船长Talk】专注数据分析 + 职场真相 + 投资洞察
