典型表现为ImportError: cannot import name 'coroutine' from 'asyncio',源于旧版motor(

motor连接MongoDB时asyncio事件循环不兼容的典型表现
Python 3.11 默认使用 asyncio.Runner,而旧版 motor(asyncio.get_event_loop(),直接运行会报 RuntimeError: There is no current event loop in thread。这不是配置问题,是 motor 版本与 Python 3.11 的底层事件循环机制冲突。
- 必须升级 motor 到
motor>=3.0.0(支持 Python 3.11 + asyncio 3.11+) - 避免在非 async 函数里调用
await client.db.collection.insert_one(...) - 不要手动调用
asyncio.get_event_loop().run_until_complete(...)—— Python 3.11 已弃用该模式
motor.AsyncIOMotorClient 初始化的正确写法
连接字符串、超时、数据库名这些参数没变,但初始化时机和上下文管理方式变了:必须确保 client 实例在 async 函数内创建,或作为全局单例在主 async 上下文中初始化。
- 推荐在应用启动时用
async def init_db():创建并缓存AsyncIOMotorClient实例 - 连接字符串中务必包含
?retryWrites=true&w=majority,否则部分写操作可能静默失败 - 不要在每次插入前新建 client —— 连接池复用失效,容易触发
ConnectionResetError
示例:
import motor.motor_asyncio
client = motor.motor_asyncio.AsyncIOMotorClient(
"mongodb://localhost:27017",
serverSelectionTimeoutMS=5000,
maxPoolSize=10
)
db = client["crawler_db"]
collection = db["pages"]
异步插入爬取数据时 await 忘记加导致的“假成功”
常见现象:代码跑完没报错,但 MongoDB 里查不到数据;或者只插入了前几条就卡住。根本原因是漏写了 await,让协程对象被丢弃,实际写入从未发生。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
-
collection.insert_one({...})返回的是协程对象,不是结果;必须await collection.insert_one(...) - 批量插入用
collection.insert_many([...])同理,必须await - 如果爬虫用
aiohttp获取页面,记得所有 I/O 操作(fetch + insert)都在同一个 async 函数里 await,不要混用loop.run_in_executor
错误写法:collection.insert_one({"url": url, "html": html})(没 await)
正确写法:await collection.insert_one({"url": url, "html": html})
并发插入时 write concern 和性能取舍
爬虫通常并发高,motor 默认的 write concern(w=1)可能让大量小文档插入变慢,但设成 w=0 又有丢数据风险。
- 开发/测试阶段可加
acknowledge=False参数跳过确认:await collection.insert_one(doc, bypass_document_validation=True)(注意:仅限非关键日志类数据) - 生产环境建议保留默认
w=1,改用insert_many批量提交(每批 100–500 条),减少网络往返 - 避免在循环里逐条
await insert_one—— 即使用了连接池,RTT 累积也会拖垮吞吐
真正卡住的地方往往不是 motor,而是你没意识到 await 是阻塞点,以及批量策略没对齐 MongoDB 的写入批次优化逻辑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










