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

如何用ThreadPoolExecutor填充字典与列表?空集合问题求助

问题解决:ThreadPoolExecutor填充字典/列表为空的问题

核心问题分析

  • 参数传递错误:executor.map会将data和exceptions当作迭代器,逐个元素传给parser函数,而非每个任务共享同一个容器对象。
  • 多线程共享WebDriver不安全:Selenium的driver实例并非线程安全,多线程共用会引发页面跳转混乱、元素查找失败等不可预期错误,进而触发异常导致数据未写入。
  • 误用multiprocessing.Manager:多线程环境下直接使用普通dict和list即可(CPython的GIL保证了部分操作的原子性,需严格线程安全可加锁),Manager是为多进程设计的,多线程中使用反而会增加额外开销或引发问题。

修复后的代码

from concurrent.futures import ThreadPoolExecutor
from selenium import webdriver
from selenium.webdriver.common.by import By
import itertools
import threading

def parser(link, data, exceptions, data_lock):
    # 每个线程创建独立的driver实例,避免多线程冲突
    driver = webdriver.Chrome()  # 根据你的浏览器类型调整(如Edge、Firefox)
    try:    
        data_ = {}
        driver.get(link)
        brand = driver.find_element(By.XPATH, '//*[@id="middle"]/section[1]/div/div/div[2]/h1').text
        name = driver.find_element(By.XPATH, '//*[@id="section-st-block9"]/div[2]/div/div[1]').find_elements(By.TAG_NAME, 'div')
        field = name[0].text
        name_ = name[1].text
        data_[field] = name_
        
        contacts = driver.find_element(By.XPATH, '//*[@id="section-st-block5"]/div[2]/div[1]').find_elements(By.CLASS_NAME, 'content-contact-item')
        for contact in contacts:
             fields = contact.find_elements(By.TAG_NAME, 'div')
             field = fields[0].text
             name__ = fields[1].text
             data_[field] = name__
        
        # 加锁保证字典修改的线程安全
        with data_lock:
            data[brand] = data_
    except Exception as e:
        # 记录异常详情,方便后续排查
        exceptions.append((link, str(e)))
    finally:
        # 确保每个driver实例都被关闭,释放资源
        driver.quit()

# 使用普通字典和列表替代Manager容器
data = {}
exceptions = []
data_lock = threading.Lock()  # 用于保证字典修改的线程安全

# 批量收集目标链接
links = []
for i in range(132, 142):
    core = 'https://fabricators.ru/zavody?region=19569&page={}'
    url = core.format(i)
    # 用临时driver收集链接,避免和线程内的driver冲突
    temp_driver = webdriver.Chrome()
    temp_driver.get(url)
    companies = temp_driver.find_elements(By.CLASS_NAME, 'content-list-item')
    for c in companies:    
        x = c.find_element(By.TAG_NAME, 'a').get_attribute('href')
        links.append(x)
    temp_driver.quit()

# 多线程执行采集任务
with ThreadPoolExecutor(5) as executor:
    # 用itertools.repeat传递同一个容器和锁给每个任务
    executor.map(parser, links, 
                 itertools.repeat(data), 
                 itertools.repeat(exceptions),
                 itertools.repeat(data_lock))

# 输出结果
print("采集到的数据:", data)
print("异常链接及详情:", exceptions)

关键修复点

  • 参数传递修正:通过itertools.repeat()确保每个parser任务都拿到同一个data、exceptions和锁对象,而非容器内的单个元素。
  • 线程独立WebDriver:每个线程创建专属的driver实例,执行完成后立即关闭,彻底避免多线程共享实例的冲突问题。
  • 线程安全保障:添加threading.Lock()确保多个线程修改字典时不会出现数据竞争,保证数据完整性。
  • 异常排查优化:捕获具体异常并记录链接和错误信息,便于定位采集失败的原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 19:13:30