You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Dask+Requests爬虫:4核仅获2倍提速的原因解析

为什么你的Dask代码只实现了2倍速度提升(而非4倍)?

看起来你碰到了Dask并行化场景里很常见的IO瓶颈问题,结合网页抓取的场景,我梳理了几个核心原因和对应的解决方案:

核心原因分析

1. 网络IO是真正的瓶颈,而非CPU

你的scrape函数里,最耗时的步骤是requests.get()——这是同步阻塞的网络请求。当Dask启动4个并发任务时,大部分时间CPU都在等待网站的响应,根本没在做计算。如果目标网站对同一IP的并发请求做了限制(比如只允许同时处理2个请求),那即使你开了4个worker,实际也只能同时跑2个任务,自然只有2倍提速。

2. Dask默认执行器的潜在限制

Dask的delayed默认使用线程执行器(dask.threaded.get),线程数默认是cpu_count()也就是4,但如果你的网络请求因为网站限流、DNS解析慢或者TCP连接开销大,线程再多也没法突破速度上限。另外,如果你之前修改过Dask的全局配置,也可能导致worker数量被意外限制。

3. 代码里的冗余操作(不影响提速,但可以优化)

顺便提一句,你的代码里有一行完全冗余的代码:

prods = [prod.text for prod in prods]

这行是重复处理已经提取过text的列表,删掉它不影响功能,还能省点微小的资源。

解决方案

方案1:控制并发请求数,适配网站限制

如果是网站限流导致的,你可以显式指定Dask的worker数量,或者给请求加小延迟,避免触发反爬机制:

# 在compute时指定worker数量,先从2个开始测试,再逐步调整
df = df.compute(scheduler='threads', num_workers=2)

# 或者给scrape函数加短延迟,降低请求频率
import time
def scrape(id):
    time.sleep(0.5)  # 加500ms延迟,避免被网站限流
    page = id+1
    start = 40*page
    bs = BeautifulSoup(requests.get(base_url.format(start,page)).text,'lxml')
    # ... 后续代码不变

方案2:改用异步请求库(更适合IO密集型抓取)

对于网页抓取这种IO密集型任务,异步请求(比如aiohttp)比同步的requests效率高很多,能更充分利用CPU的空闲时间。结合Dask的异步执行器,能最大化并发能力:

import aiohttp
import asyncio

async def async_scrape(id):
    page = id+1
    start = 40*page
    async with aiohttp.ClientSession() as session:
        async with session.get(base_url.format(start,page)) as resp:
            text = await resp.text()
            bs = BeautifulSoup(text,'lxml')
            prods = [prod.text for prod in bs.find_all('span',attrs={'class':'product-description js-ellipsis'})]
            brands = [b.text for b in bs.find_all('span',attrs={'class':'product-name'})]
            return pd.DataFrame({'product': prods, 'brand': brands})

# 用Dask的异步执行器运行
data = [dask.delayed(async_scrape)(id) for id in range(10)]
df = dask.delayed(pd.concat)(data)
df = df.compute(scheduler='asyncio', num_workers=4)

方案3:复用HTTP连接

用requests.Session()来复用TCP连接,减少每次请求的握手开销,提升整体速度:

# 提前创建session,传给scrape函数
session = requests.Session()

def scrape(id, session):
    page = id+1
    start = 40*page
    bs = BeautifulSoup(session.get(base_url.format(start,page)).text,'lxml')
    # ... 后续代码不变

data = [dask.delayed(scrape)(id, session) for id in range(10)]

内容的提问来源于stack exchange,提问作者Sergio Lucero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:03:35