Python Selenium使用Pool多进程下载时如何获取各进程对应文件路径
多进程池Chromium下载冲突解决方案
现有逻辑只适用于单进程独占下载目录的场景,多进程共用目录时会出现两个核心问题:一是全局扫描.crdownload后缀无法区分归属进程,二是按创建时间取最新文件的逻辑在并发完成时会匹配错误。可以通过以下两种方案解决:
方案1:为每个进程分配独立下载目录(最推荐)
该方案实现成本最低,无任何冲突概率,适配headless Chromium的配置逻辑:
- 每个子进程启动后,生成唯一的临时下载目录,可通过进程ID、UUID拼接目录名,示例:
tmp_download_dir = f"./tmp_download_{os.getpid()}",创建目录后即可使用 - 初始化当前进程的Chromium实例时,单独配置
prefs中的download.default_directory为上述专属临时目录 - 原有
download_wait、last_downloaded_file逻辑无需修改,因为目录为当前进程独占,不会出现其他进程的文件干扰 - 下载完成后将目标文件移动到全局存储目录,再删除临时目录释放空间即可
方案2:共用全局下载目录的适配方案
如果业务要求必须使用同一个下载目录,可从下载任务绑定层面改造逻辑:
优化思路
放弃全局扫描后缀+匹配最新创建时间的模糊逻辑,直接将下载任务和对应的文件绑定,可通过两种方式实现:
- 对接Chromium DevTools协议监听下载事件:开启Chromium的远程调试能力,触发下载前注册监听回调,可直接获取当前下载任务的文件名、进度、完成状态,无需扫描目录。如果使用上层封装工具,可直接调用对应接口:
- Selenium4+ 支持直接获取下载任务对象
- Playwright可通过
expect_download接口直接等待当前触发的下载完成,示例:with page.expect_download() as download_info: page.locator("#download_btn").click() download = download_info.value # 直接获取当前下载的文件路径,完全不会和其他进程的任务冲突 file_path = download.path()
- 改造原有轮询逻辑:如果暂时不想对接DevTools,可修改原有函数,先捕获当前进程触发的专属临时下载文件,再等待其下载完成,改造后代码示例:
import os import time def download_wait(path_to_downloads): # 记录触发下载前已有的临时文件,过滤不属于当前任务的下载 pre_exist_cr = set(f for f in os.listdir(path_to_downloads) if f.endswith('.crdownload')) wait_seconds = 0 target_cr = None # 先等待当前任务的临时下载文件出现 while not target_cr and wait_seconds < 30: time.sleep(0.5) cur_cr = set(f for f in os.listdir(path_to_downloads) if f.endswith('.crdownload')) new_cr = cur_cr - pre_exist_cr if len(new_cr) == 1: target_cr = new_cr.pop() wait_seconds += 0.5 if not target_cr: raise TimeoutError("未检测到下载任务启动") # 仅等待当前任务的临时文件消失,代表下载完成 wait_seconds = 0 while wait_seconds < 270: time.sleep(1) if target_cr not in os.listdir(path_to_downloads): break wait_seconds += 1 else: raise TimeoutError("下载超时") # 直接返回对应目标文件名,无需匹配创建时间 target_file = target_cr.rsplit('.crdownload', 1)[0] print(f'文件 {target_file} 下载完成,耗时 {wait_seconds + 30} 秒') return target_file
注意:如果业务本身会返回同名文件,可在下载完成后给文件名拼接进程ID、任务ID等唯一标识,避免文件覆盖。
内容的提问来源于stack exchange,提问作者Razilator
相关产品推荐
相关产品推荐

