Django视图中BeautifulSoup爬取速度慢,本地运行正常问题咨询
嘿,我太懂这种同一代码在不同环境下速度差十倍的挫败感了!咱们一步步拆解原因,搞定这个问题:
一、先优化请求库的连接复用
你当前用的requests/urllib2可能没利用连接池会话复用。Jupyter里运行时会隐式复用TCP连接,但Django每次处理请求都是全新上下文,每次爬取都要重新握手建连,这会凭空增加大量耗时。
试试改成用requests.Session()来复用连接:
import requests from bs4 import BeautifulSoup def get_soup(url, header): # 创建会话,复用连接池 session = requests.Session() # 配置连接池大小,减少重复建连开销 session.mount('http://', requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)) session.mount('https://', requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)) # 设置超时,避免无限等待拖慢进程 response = session.get(url, headers=header, timeout=5) soup = BeautifulSoup(response.text, 'html.parser') return soup
这一步往往是跨环境速度差异的核心原因,改完后耗时应该会大幅下降。
二、检查Django调试模式的影响
如果你的Django还开着DEBUG = True,调试模式会开启一堆额外检查(比如SQL查询日志、静态文件自动重载),这些都会拖慢视图响应。可以临时把DEBUG改成False测试,看看耗时是否明显降低。
三、用异步处理解放视图阻塞
Django同步视图会在爬取过程中完全阻塞,而Jupyter是单进程单任务,没有其他请求干扰。如果爬取是高频操作,建议把爬取逻辑放到异步任务队列(比如Celery)里,视图只负责触发任务,前端再通过轮询或WebSocket获取结果,用户就不用等10秒才能看到页面了。
要是用的是Django 3.1+,也可以直接写异步视图:
from django.http import HttpResponse import asyncio import aiohttp from bs4 import BeautifulSoup async def fetch(session, url, header): async with session.get(url, headers=header, timeout=5) as response: return await response.text() async def async_get_soup(url, header): async with aiohttp.ClientSession() as session: html = await fetch(session, url, header) return BeautifulSoup(html, 'html.parser') async def my_view(request): # 替换成你的目标URL和请求头 target_url = "你要爬的网页地址" request_header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."} soup = await async_get_soup(target_url, request_header) # 这里处理soup提取图片src的逻辑 img_srcs = [img.get('src') for img in soup.find_all('img') if img.get('src')] return HttpResponse(f"获取到图片地址:{','.join(img_srcs)}")
异步请求库aiohttp在IO密集型任务上比同步requests效率高很多,特别适合爬取这类网络操作。
四、给爬取结果加缓存
如果同一个URL会被多次请求,直接缓存爬取后的soup或图片src列表,能彻底避免重复爬取。用Django自带的缓存框架就行:
from django.core.cache import cache def get_soup(url, header): cache_key = f"crawled_soup_{url}" soup = cache.get(cache_key) if not soup: # 这里放优化后的爬取逻辑 session = requests.Session() response = session.get(url, headers=header, timeout=5) soup = BeautifulSoup(response.text, 'html.parser') # 缓存1小时(3600秒),可根据需求调整 cache.set(cache_key, soup, 3600) return soup
重复请求同一URL时直接从缓存取,速度会快到离谱。
五、排查网络环境差异
Jupyter和Django服务器的网络环境可能不一样:比如Jupyter用了代理、DNS解析更快,或者Django服务器带宽受限。可以在Django视图里加个简单测试,比如请求百度看看耗时,对比Jupyter里的耗时,确认是不是网络环境的锅。
内容的提问来源于stack exchange,提问作者Mateus Denucci

