基于Django的异步爬虫实时数据推送方案选型与问题咨询
方案选型与SSE接入问题解决
现有方案优劣对比
- 多进程+Pipe:爬虫能完全独立运行,不占用Django进程资源,但Pipe是点对点通信,无法直接实现多客户端的自动数据推送,用户端仍需手动刷新或额外做轮询,不符合自动更新需求,维护成本也高。
- WebSockets:成熟的双向通信方案,但你仅需服务器单向推送数据,用WebSocket会冗余,增加不必要的复杂度,不如SSE轻量。
- SSE:完全匹配你的单向推送场景,浏览器原生支持EventSource,客户端实现简单,服务器端开销小,确实是最优选择。你遇到的
RuntimeError: asyncio.run() cannot be called from a running event loop,是因为Daphne已在运行asyncio事件循环,你重复调用asyncio.run()启动爬虫导致冲突。
SSE接入爬虫的正确姿势
解决事件循环冲突问题
不要用asyncio.run()启动异步爬虫,直接将爬虫任务加入当前运行的事件循环:
import asyncio async def crawler_task(broadcast_queue): while True: # 采集、处理数据逻辑 data = await fetch_and_process_data() # 将数据放入广播队列 await broadcast_queue.put(data) # 模拟采集间隔 await asyncio.sleep(60) # 在SSE视图中获取当前事件循环并启动爬虫(确保只启动一次) loop = asyncio.get_running_loop() # 用asyncio.Queue做数据广播的中间容器 broadcast_queue = asyncio.Queue() # 启动爬虫任务 loop.create_task(crawler_task(broadcast_queue))
实现多客户端数据推送
需要全局广播机制,让爬虫数据能推给所有订阅的SSE客户端:
- 维护一个客户端连接列表(或用asyncio.Queue做广播),每个SSE客户端连接时加入列表。
- 爬虫每次生成数据后,遍历列表给每个客户端发送SSE格式的消息。
如果你的爬虫是同步while True循环,不能直接放入异步事件循环(会阻塞),可将同步爬虫放到独立线程运行,线程将数据写入线程安全队列,SSE视图从队列读取数据推给客户端:
import threading import queue import time def sync_crawler(broadcast_queue): while True: # 同步采集、处理数据 data = sync_fetch_and_process() broadcast_queue.put(data) # 采集间隔 time.sleep(60) # 线程安全队列 broadcast_queue = queue.Queue() # 启动爬虫线程 threading.Thread(target=sync_crawler, args=(broadcast_queue,), daemon=True).start()
遗漏的可选方案
Celery+消息中间件(Redis/RabbitMQ):
- 将持续运行的爬虫作为Celery常驻任务,爬虫生成数据后发送到Redis Pub/Sub频道或RabbitMQ队列。
- Django的SSE视图订阅对应频道/队列,收到数据后立即推给客户端。
- 该方案解耦性更强,爬虫、Django服务、消息中间件可独立部署,扩展性更好,适合后期用户量增长的场景。
内容的提问来源于stack exchange,提问作者Arbuz
相关产品推荐
相关产品推荐

