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

Python使用ThreadPoolExecutor多线程爬虫内存不足进程被Kill问题求助

问题核心原因
  1. futures_done集合只增不减:调用future.result()后未将对应future从集合中删除,数百万个已处理的future对象长期驻留内存,最终占满资源。
  2. 遍历逻辑效率极低:每次新增已完成任务时都会全量遍历futures_done集合,集合规模越大遍历耗时越高,直接导致程序运行速度持续下降。
  3. 任务队列无限制膨胀:现有逻辑无法限制待执行任务的总量,提交任务的速度远快于任务处理速度,大量待执行任务持有host等资源持续占用内存。
  4. 资源未及时释放:requests响应对象、lxml解析树没有主动释放,高并发场景下GC回收不及时会进一步拉高内存占用。
优化方案
  • 处理完单个future后立即将其从集合中移除,避免无效对象堆积
  • 限制待执行任务的数量,保持待处理任务数和工作线程数处于同一量级,不要一次性提交数百万个任务
  • 无需累计到1000个才处理结果,每完成一个任务就直接获取结果释放资源
  • 用concurrent.futures.as_completed替代手动维护两个future集合,简化逻辑降低出错概率
  • 可选优化:换用异步HTTP库(如httpx、aiohttp)替代多线程+requests,内存占用会低很多
修正后可运行代码
import sys
import requests
import concurrent.futures
import urllib3
from lxml.html import fromstring
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def title(host):
    try:
        url = "https://" + host
        # 增加stream=True减少大响应内存占用
        r = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=3, verify=False, stream=True)
        tree = fromstring(r.content.decode('utf-8', errors='ignore'))
        title = tree.findtext('.//title')
        res = f"{host}: {title}"
        # 主动释放资源
        del tree, r
        return res
    except:
        return None

if __name__ == "__main__":
    max_workers = int(sys.argv[2])
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {}
        with open(sys.argv[1], 'r', encoding='utf-8') as f:
            for line in f:
                host = line.strip()
                if not host:
                    continue
                # 限制待执行任务数不超过工作线程数的2倍,避免队列膨胀
                while len(futures) >= max_workers * 2:
                    # 等待任意一个任务完成
                    done, _ = concurrent.futures.wait(futures.keys(), return_when=concurrent.futures.FIRST_COMPLETED)
                    for future in done:
                        res = future.result()
                        if res:
                            print(res)
                        # 完成后立即删除future释放内存
                        del futures[future]
                # 提交新任务
                future = executor.submit(title, host)
                futures[future] = host
            # 处理剩余所有未完成的任务
            for future in concurrent.futures.as_completed(futures.keys()):
                res = future.result()
                if res:
                    print(res)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 10:27:02