Selenium 文件上传和下载 | 菜鸟教程这篇文章讲了Selenium4 文件上传和下载的基本知识点,我建议你先看完上面那篇文章学习基础,然后再学习下面的。
一、文件上传
1. 基本文件上传(使用 send_keys)
对于标准的 <input type="file">
元素,最简单的方法是直接使用 send_keys()
发送文件路径:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com/upload")
# 找到文件上传输入框
file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']")
# 发送文件绝对路径
file_input.send_keys("/path/to/your/file.txt")
# 点击上传按钮(如果有)
upload_button = driver.find_element(By.ID, "upload-button")
upload_button.click()
2. 处理隐藏的文件输入框
有些网站使用自定义样式隐藏了原生的文件输入框,然后通过 JavaScript 触发文件选择。对于这种情况:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/upload")
# 找到隐藏的文件输入框(可能需要查看页面源代码)
hidden_file_input = driver.find_element(By.ID, "hidden-file-input")
# 使用JavaScript使元素可见(如果需要)
driver.execute_script("arguments[0].style.display = 'block';", hidden_file_input)
# 发送文件路径
hidden_file_input.send_keys("/path/to/your/file.txt")
3. 使用 AutoIT 或 Sikuli 处理系统对话框(不推荐)
对于某些无法直接通过 Selenium 操作的系统级文件选择对话框,可以考虑使用 AutoIT 或 Sikuli,但这些方法不够稳定且跨平台性差,应尽量避免。
4. 多文件上传
如果需要上传多个文件,可以发送多个文件路径,用换行符分隔:
python
# 上传多个文件
file_input.send_keys("/path/to/file1.txt\n/path/to/file2.txt\n/path/to/file3.txt")
二、文件下载
1. 配置浏览器下载选项
在 Selenium 4 中,我们可以通过浏览器选项来配置下载行为:
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 设置下载选项
chrome_options = Options()
# 设置下载路径
download_dir = "/path/to/download/directory"
# Chrome 浏览器配置
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False, # 禁止弹出下载对话框
"download.directory_upgrade": True,
"safebrowsing.enabled": True # 禁用安全浏览,以避免下载延迟
}
chrome_options.add_experimental_option("prefs", prefs)
# 创建浏览器实例
driver = webdriver.Chrome(options=chrome_options)
2. 等待下载完成
下载文件后,我们需要等待下载完成:
python
import os
import time
def wait_for_download_complete(download_dir, timeout=30, check_interval=1):
"""
等待下载目录中的 .crdownload 文件消失(Chrome)
"""
end_time = time.time() + timeout
while time.time() < end_time:
# 检查是否有未完成的下载文件(Chrome使用.crdownload扩展名)
if not any(fname.endswith('.crdownload') for fname in os.listdir(download_dir)):
return True
time.sleep(check_interval)
return False
# 点击下载链接
download_link = driver.find_element(By.ID, "download-link")
download_link.click()
# 等待下载完成
if wait_for_download_complete(download_dir):
print("下载完成")
else:
print("下载超时")
3. 获取下载的文件信息
python
def get_downloaded_file_info(download_dir, pattern=None):
"""
获取下载目录中的文件信息
"""
files = []
for filename in os.listdir(download_dir):
filepath = os.path.join(download_dir, filename)
if os.path.isfile(filepath):
# 如果指定了模式,只返回匹配的文件
if pattern and not pattern in filename:
continue
files.append({
"name": filename,
"path": filepath,
"size": os.path.getsize(filepath),
"modified": os.path.getmtime(filepath)
})
# 按修改时间排序,最新的在前
files.sort(key=lambda x: x["modified"], reverse=True)
return files
# 获取所有下载的文件
downloaded_files = get_downloaded_file_info(download_dir)
# 获取最新下载的文件
if downloaded_files:
latest_file = downloaded_files[0]
print(f"最新下载的文件: {latest_file['name']}, 大小: {latest_file['size']} 字节")
4. 处理不同浏览器的下载
不同浏览器需要不同的配置:
Chrome 浏览器
python
chrome_options = Options()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
chrome_options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=chrome_options)
Firefox 浏览器
python
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
firefox_options = Options()
firefox_options.set_preference("browser.download.folderList", 2) # 0=桌面, 1=默认, 2=自定义
firefox_options.set_preference("browser.download.dir", download_dir)
firefox_options.set_preference("browser.download.useDownloadDir", True)
firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream,application/pdf,text/csv")
firefox_options.set_preference("pdfjs.disabled", True) # 禁用内置PDF查看器
driver = webdriver.Firefox(options=firefox_options)
Edge 浏览器
python
from selenium.webdriver.edge.options import Options
edge_options = Options()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
edge_options.add_experimental_option("prefs", prefs)
driver = webdriver.Edge(options=edge_options)
三、实战示例:完整的文件上传和下载流程
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import os
import time
# 设置下载目录
download_dir = os.path.abspath("./downloads")
if not os.path.exists(download_dir):
os.makedirs(download_dir)
# 配置浏览器选项
chrome_options = Options()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
chrome_options.add_experimental_option("prefs", prefs)
# 创建浏览器实例
driver = webdriver.Chrome(options=chrome_options)
wait = WebDriverWait(driver, 10)
try:
# 1. 上传文件
driver.get("https://example.com/upload")
# 找到并上传文件
file_input = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='file']"))
)
file_input.send_keys(os.path.abspath("./test_upload.txt"))
# 点击上传按钮
upload_button = driver.find_element(By.ID, "upload-btn")
upload_button.click()
# 等待上传完成
wait.until(EC.text_to_be_present_in_element((By.ID, "status"), "上传成功"))
# 2. 下载文件
download_link = wait.until(
EC.element_to_be_clickable((By.ID, "download-link"))
)
download_link.click()
# 等待下载完成
def is_download_complete():
# 检查是否有.crdownload临时文件
for fname in os.listdir(download_dir):
if fname.endswith('.crdownload'):
return False
return True
wait.until(lambda driver: is_download_complete())
# 获取下载的文件
downloaded_files = [f for f in os.listdir(download_dir)
if os.path.isfile(os.path.join(download_dir, f))]
if downloaded_files:
latest_file = max(downloaded_files,
key=lambda f: os.path.getmtime(os.path.join(download_dir, f)))
print(f"成功下载文件: {latest_file}")
# 验证文件内容(如果需要)
file_path = os.path.join(download_dir, latest_file)
with open(file_path, 'r') as f:
content = f.read()
print(f"文件内容: {content[:100]}...") # 只打印前100个字符
finally:
driver.quit()
四、常见问题与解决方案
1. 下载对话框弹出
如果仍然看到下载对话框,确保正确设置了浏览器偏好设置:
python
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False, # 关键设置
# ...
}
2. 下载文件名为乱码
某些情况下,下载的文件名可能包含乱码。可以尝试重命名文件:
python
import urllib.parse
# 从Content-Disposition头获取文件名(如果可用)
content_disposition = driver.execute_script(
"return arguments[0].getAttribute('download')", download_link
)
if content_disposition:
filename = urllib.parse.unquote(content_disposition)
# 重命名文件
latest_file = get_latest_downloaded_file(download_dir)
os.rename(latest_file, os.path.join(download_dir, filename))
3. 处理大文件下载
对于大文件下载,可能需要增加超时时间:
python
def wait_for_download_complete(download_dir, timeout=300): # 5分钟超时
# ... 实现同上
4. 清理下载目录
在测试开始前清理下载目录,避免旧文件干扰:
python
import shutil
# 清理下载目录
if os.path.exists(download_dir):
shutil.rmtree(download_dir)
os.makedirs(download_dir)
五、最佳实践
-
使用绝对路径:始终使用文件的绝对路径,避免相对路径可能带来的问题。
-
合理设置超时:根据文件大小和网络状况设置合理的超时时间。
-
验证下载内容:不仅检查文件是否存在,还应验证文件内容和大小。
-
清理测试环境:测试开始前清理下载目录,确保测试环境干净。
-
跨浏览器考虑:不同浏览器需要不同的配置,确保你的代码能处理这些差异。
-
错误处理:添加适当的异常处理,处理下载失败或超时的情况。