如何防止Python Selenium Chrome无头模式下载文件覆盖已有文件
Selenium Chrome无头模式下载重名文件自动覆盖问题
问题现象
- 有头(非headless)模式下下载行为符合预期:
- 点击下载按钮后无弹窗,自动下载默认名为
P3MLF.xls的Excel文件 - 下载目录存在同名文件时,自动将新文件命名为
P3MLF(xx).xls,xx为目录内同系列文件的最大递增收尾序号,不会覆盖旧文件
- 点击下载按钮后无弹窗,自动下载默认名为
- 切换到headless模式后行为异常:
- 下载文件始终使用
P3MLF.xls命名,直接覆盖目录下已有同名文件,不会自动生成带序号的新文件名
下载命名规则示例
- 下载文件始终使用
当前使用的实现代码如下:
def download(self): self.opt = webdriver.ChromeOptions() self.opt.add_experimental_option('detach', True) self.opt.add_argument('headless') self.opt.add_argument('window-size=1920x1080') self.opt.add_argument('disable-gpu') self.opt.add_argument('--enable-javascript') self.ser = Service('SupportingFiles\chromedriver.exe') self.driver = webdriver.Chrome(options=self.opt, service=self.ser) self.prefs = { 'profile.default_content_settings.popups': 0, 'download.default_directory': 'C:\\Drive_D\\Download', #this part actually not wowking 'download.prompt_for_download': False, 'download.directory_upgrade': True } self.opt.add_experimental_option("prefs", self.prefs) self.driver.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command') params = {'cmd':'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': self.dlPath_box.get()}} self.driver.execute('send_command', params)
问题原因
- 你当前使用的
--headless是Chrome旧版无头实现(Chrome 112版本前的原生无头模式),该模式没有复用有头模式的完整下载管理逻辑,本身不支持重名文件自动重命名。 - 代码中通过
Page.setDownloadBehavior强制开启下载、指定下载路径的接口,是专门给旧版无头模式做的兼容方案,该接口会直接将下载流写入指定路径,完全绕过Chrome下载模块的重名校验、自动编号流程,所以必然会覆盖同名文件。 - 额外的代码逻辑问题:你是在初始化
webdriver.Chrome实例之后,才把下载相关的prefs配置添加到ChromeOptions中,这时候配置已经不会被driver加载,这也是你注释里写download.default_directory配置不生效的原因。
解决方案
优先方案:使用Chrome新版无头模式(Chrome 112及以上版本支持)
Chrome 112版本后推出了和有头模式运行逻辑完全一致的新版无头模式,所有下载规则、文件命名逻辑、弹窗行为和有头模式完全相同,不需要调用Page.setDownloadBehavior做特殊兼容。
你只需要做两处修改:
- 将启动参数中的
--headless替换为--headless=new - 把prefs配置的添加逻辑移到driver初始化之前,移除手动调用
Page.setDownloadBehavior的冗余代码
修正后的可运行代码:
def download(self): self.opt = webdriver.ChromeOptions() self.opt.add_experimental_option('detach', True) # 替换为新版无头模式,行为和有头模式完全一致 self.opt.add_argument('--headless=new') self.opt.add_argument('window-size=1920x1080') self.opt.add_argument('disable-gpu') self.opt.add_argument('--enable-javascript') # 所有prefs配置必须在初始化driver之前添加,才会生效 self.prefs = { 'profile.default_content_settings.popups': 0, 'download.default_directory': self.dlPath_box.get(), 'download.prompt_for_download': False, 'download.directory_upgrade': True } self.opt.add_experimental_option("prefs", self.prefs) self.ser = Service('SupportingFiles\chromedriver.exe') # 直接初始化driver即可,不需要额外调用send_command设置下载行为 self.driver = webdriver.Chrome(options=self.opt, service=self.ser)
使用该配置后,无头模式下的下载命名逻辑会和有头模式完全一致,自动处理重名文件的序号命名,不会覆盖旧文件。
兼容方案(Chrome版本低于112、无法使用新版无头时使用)
如果你的环境无法升级Chrome,只能使用旧版无头模式,就需要自行实现重名文件处理逻辑,步骤如下:
- 触发下载前,先扫描目标下载目录,统计所有文件名匹配
P3MLF(*).xls规则的文件,提取序号计算出当前可用的下一个序号 - 触发下载后,监听下载目录,等待临时下载文件(后缀为
.crdownload)写入完成、后缀变为.xls且文件不再被进程占用 - 检查下载目录下是否存在历史
P3MLF.xls文件,如果存在,将刚下载完成的P3MLF.xls重命名为P3MLF(计算好的序号).xls即可。
内容的提问来源于stack exchange,提问作者Ken eXs
相关产品推荐
相关产品推荐

