如何用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
相关产品推荐
相关产品推荐

