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

Python requests与concurrent.futures多线程爬取时as_completed内打印不执行问题

问题排查与修复方案

核心问题原因

  • 全局变量竞态:你的scrape函数直接读写全局offers变量,多线程环境下未加锁的全局可变对象读写会触发竞态条件,轻则返回数据错误,重则导致线程阻塞、流程中断,这是后续不再打印total的主要原因。
  • 错误的返回值逻辑:scrape函数应该返回当前页解析得到的单页offer列表,你当前的代码直接返回全局offers,相当于每次result()拿到的是重复累加的全量列表,逻辑完全错误。
  • 无异常捕获机制:requests请求、bs4解析过程中如果出现超时、4xx/5xx状态码、DOM结构异常等问题,scrape会直接抛出异常,该异常会在result()调用时触发,没有捕获的情况下会直接中断as_completed循环,自然不会输出后续的total。

修复后代码示例

scrape函数修改

import requests
from bs4 import BeautifulSoup

def scrape(i):
    try:
        # 添加超时设置,避免无限等待
        resp = requests.get(f'somepage.com/page{i}', timeout=10)
        # 校验请求状态,遇到4xx/5xx直接抛出异常
        resp.raise_for_status()
        soup = BeautifulSoup(resp.text, 'lxml')
        # 替换为你自己的单页offer解析逻辑,仅返回当前页的offer列表
        page_offers = [a['href'] for a in soup.select('a.offer-link')]
        # 仅打印当前页的长度,不触碰全局变量
        print(len(page_offers))
        return page_offers
    except Exception as e:
        # 异常兜底,出错返回空列表,不中断主流程
        print(f"第{i}页爬取失败: {str(e)}")
        return []

主逻辑修改

import concurrent.futures

offers = []
processes = []
# 替换为你的实际总页数
total_page = 20

with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:
    for i in range(total_page):
        processes.append(executor.submit(scrape, i))
    for future in concurrent.futures.as_completed(processes):
        # 单线程累加,不存在并发安全问题
        offers += future.result()
        print('total:', len(offers))

额外说明

修复后每个线程完全独立处理单页逻辑,和全局变量无耦合,同时异常兜底保证哪怕单页爬取失败也不会中断整个流程,as_completed循环可以正常执行完所有任务,不会再出现后续不打印total的问题。

内容的提问来源于stack exchange,提问作者Maciej Miecznik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 07:09:04