Web上本地上传图片,弹出的框Selenium是无法识别的,也就是说,selenium本身没有直接的方法去实现上传本地文件,这里总结了两种上传文件的方式。
一、利用Robot类处理文件上传。
其大致流程可以为:
1、 利用selenium点击web上本地文件的上传按钮;
2、 在弹出的弹框中,文件路径输入框默认的是光标的聚焦,将文件在磁盘上的路径通过拷贝和黏贴的方法写上去。
3、 通过按下回车,默认触发弹框的确定按钮,完成文件上传的功能。
这里以百度首页的利用图片搜索为例:
打开百度首页,搜索按钮左侧有一个照相机的图标,点击可以选择图片搜索,我们通过本地上传图片的过程来模拟文件自动化上传操作。准备条件,在百度图片搜索一个图片,保存到桌面,例如找到一个关于selenium的图片,然后保存在桌面,名称为selenium.jpg。
相关实现代码如下:
package first;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyEvent;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class shangchuang {
public static void main(String[] args)throws Exception {
WebDriver driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(4, TimeUnit.SECONDS);
driver.get(“https://www.baidu.com”);
//指定图片路径
StringSelection selection=new StringSelection(“C:\\Users\\你的用户名\\Desktop\\selenium.jpg”);
//把图片路径复制到剪切板
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null);
System.out.println(“selection”+selection);
//点击照相机这个工具
driver.findElement(By.xpath(“//*/span[@class=’soutu-btn’]”)).click();
//点击本地上传图片
driver.findElement(By.xpath(“//*/div[@class=’upload-wrap’]”)).click();
//新建一个Robot类的对象
Robot robot=new Robot();
Thread.sleep(1000);
//按下Ctrl+V
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_V);
//释放Ctrl+V
robot.keyRelease(KeyEvent.VK_CONTROL);
robot.keyRelease(KeyEvent.VK_V);
Thread.sleep(2000);
//点击回车
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
}
}
二、利用AutoIt上传文件
以上是第一种方的实现,第二种方式是利用AutoIt这个工具。这是一个能支持桌面GUI自动化的工具,它支持脚本语言编写。在Selenium脚本中如果需要AutoIt来协助这个文件上传功能,大概步骤是这样的:
1. Selenium点击web产品上的文件上传按钮,弹窗上传框。
2.执行AutoIt实现准备好的脚本文件,这个脚本文件写了关于上传什么文件的一个.exe文件。
在一切测试工作之前,我们先下载和安装AutoIt。
1)打开AutoIt的官网下载地址
https://www.autoitscript.com/site/autoit/downloads/
2)点击下载zip,当然也可以下载Editor。
解压得到的效果如图:
3)点击SciTe文件夹,我们打开脚本编辑器。双击SciTE.exe
4)打开百度图片上传窗口,同时打开AutoIt 脚本编辑器和元素定位器。拖动元素定位器上那个靶点形状按钮到文件上传弹窗,能够捕获到一些元素信息。
5)在AutoIt脚本编辑器里输入如下脚本,绿色部分为解释的,不需要写。
6)编译成一个.exe文件
先保存到本地,例如默认路径保存,名称为UploadFile.au3,然后在AutoIt脚本编辑器中点击Tools菜单,选择compile,会在同路径下生成一个UploadFile.exe的文件,待会在Selenium脚本要使用。
7)Selenium脚本执行UploadFile.exe文件,观察文件是否上传。
package first;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class AutoIt {
public static void main(String[] args) throws Exception{
WebDriver driver=new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(4, TimeUnit.SECONDS);
driver.get(“http://www.baidu.com”);
//点击照相机这个工具
driver.findElement(By.xpath(“//*/span[@class=’soutu-btn’]”)).click();
//点击本地上传图片
driver.findElement(By.xpath(“//*/div[@class=’upload-wrap’]”)).click();
// 执行桌面的AutoIt封装的脚本
Runtime.getRuntime().exec(“C:\\Users\\你的用户名\\Desktop\\UploadFile.exe”);
}
}
我用的是火狐62,最终的效果如图所示:
三、把文件信息上传给选择按钮(针对input标签)。
以下是常见页面形式:
分析HTML,发现无论是普通前台还是Extjs,他们的html有一个共同的特点,具有以下代码:
<input ..... type="file" ...../>
ok,只要我们使用selenium自带的sendkeys把文件路径(相对绝对无所谓)传给该元素,就可绕过选择窗口直接上传文件.
解决方案诞生了
driver.findElement(XXXXX).sendKeys("filePath");
Have a Try
博客园上传图片:driver.findElement(By.name(“file”)).sendKeys(“C:\Users\123\Pictures\a.png”);
QQ邮箱上传附件:driver.findElement(By.name(“UploadFile”)).sendKeys(“C:\Users\123\Pictures\a.png”);
定位元素根据情况而定,不一定用name;文件路径可以使相对路径,大家自行修改就好
注意:执行sendKeys的元素一定要符合input和 type=”file”条件,否则就是你没找对上传文件的对象,会上传失败的。
像这样的,一定注意不要上传到前面的那个input上,否则会失败的
四、使用pywin32 库(针对非input标签)。
引用文章:https://blog.csdn.net/zyooooxie/article/details/88141601
# 使用win32gui和win32con对弹出的文件窗口进行控制:输入要上传的文件路径,点击打开
# 获取文件弹窗对象【第一个对象句柄,后面的父】
dialog = win32gui.FindWindow('#32770', '打开') # Chrome 记得配套
# dialog = win32gui.FindWindow('#32770', '文件上传') # FireFox 记得配套
# IE为'选择要加载的文件',Chrome为'打开',火狐为“文件上传”。
ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
ComboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, 'ComboBox', None)
# 获取文件路径输入框对象的句柄
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)
# 获取 打开button按钮对象
button = win32gui.FindWindowEx(dialog, 0, 'Button', '打开(&O)')
time.sleep(1)
# 输入框输入绝对路径
win32gui.SendMessage(Edit, win32con.WM_SETTEXT, 0, r'd:\60x91.JPG')
time.sleep(3) # 方便观看过程
# 点击 打开
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)
time.sleep(3) # 方便观看过程
自己写的一段代码,验证可行:
__author__ = "MrBean"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from time import sleep,time
import win32con,win32api,win32gui
import win32clipboard as w
# driver = webdriver.Chrome()
# driver_type = 1
driver = webdriver.Firefox()
driver_type = 0
driver.maximize_window()
"""
# 读取写入剪切板还可以用pyperclip
# pip install pyperclip
# pyperclip.copy('dsd')#向剪切板发送文本
# pyperclip.paste() #读取剪切板文本
"""
#
# def getClipText(): # 读取剪切板
# w.OpenClipboard()
# d = w.GetClipboardData(win32con.CF_TEXT)
# w.CloseClipboard()
# return d
#
#
# def setClipText(aString): # 写入剪切板
# w.OpenClipboard()
# w.EmptyClipboard()
# w.SetClipboardData(win32con.CF_TEXT, aString)
# w.CloseClipboard()
try:
"""
#模拟天翼云盘登录
#切换iframe
# switch_to_frame可以直接用id或name定位切换
#如果无id和name,可以通过tag先定位到
#iframe = driver.find_element_by_tag_name("iframe")
# drivelr.swirch_to_rrame{ iframe)
# 当iframe上的操作完后,想重新回到主页面上操作元素,这时候,就可以用switch_to_default_content()方法返回上一层表单
"""
driver.get("https://cloud.189.cn")
driver.switch_to.frame("udb_login")
# 输入账号密码
driver.find_element_by_id("userName").clear()
driver.find_element_by_id("userName").send_keys("********")
sleep(2)
driver.find_element_by_id("password").clear()
driver.find_element_by_id("password").send_keys("********")
sleep(2)
driver.find_element_by_id("j-login").click()
# cookie = driver.get_cookies()
# print(cookie)
driver.switch_to.default_content()
sleep(3)
# 转到新页面
# curr_window = driver.current_window_handle
sleep(2)
file_path = "C:\PyQtProj\ToExe\cardpic.py"
#针对input标签的文件上传,直接通过send_keys,chrome及firefox皆可用
# driver.find_element_by_xpath("/html/body/div[3]/div[2]/div[5]/div[1]/div[1]/div/input").send_keys(file_path)
#针对非input标签或不用send_keys方法,可采用win32gui
#天翼云盘在firefox上支持有问题,无法通过鼠标悬停在“上传”上显示下拉列表,chrome正常
if driver_type:
# 模拟鼠标悬停在“上传”
upload_btn = driver.find_element_by_id("uploadifive-J_AddFileDiv")
ActionChains(driver).move_to_element(upload_btn).perform()
upload_file = driver.find_element_by_id("J_AddFile")
upload_file.click()
sleep(1)
# win32gui
dialog = win32gui.FindWindow('#32770', '打开') # chrome的文件对话框
else:
upload_btn = driver.find_element_by_id("uploadifive-J_AddFileDiv")
upload_btn.click()
sleep(1)
# win32gui
dialog = win32gui.FindWindow('#32770', '文件上传') # firefox的文件对话框
ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
ComboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, 'ComboBox', None)
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None) # 上面三句依次寻找对象,直到找到输入框Edit对象的句柄
button = win32gui.FindWindowEx(dialog, 0, 'Button', None) # 确定按钮Button
win32gui.SendMessage(Edit, win32con.WM_SETTEXT, None, file_path) # 往输入框输入绝对地址
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button) # 按button
"""
百度网盘模拟登录
"""
# driver.get("https://pan.baidu.com/")
#
# """
# # 延时2秒,否则会因为浏览器加载页面未完成而出现无法定位到<账号密码登录>:
# # Unable to locate element: {"method":"id","selector":"TANGRAM__PSP_4__footerULoginBtn"}
# """
#
# """
# # 方法1:强制等待time.sleep
# """
# # sleep(2)
# #
# # #模拟点击”账号密码登录“
# # driver.find_element_by_id("TANGRAM__PSP_4__footerULoginBtn").click()
#
# """
# # 方法2:显式等待WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
# # driver:浏览器驱动
# # timeout:最长超时时间,默认以秒为单位
# # poll_frequency:检测的间隔步长,默认为0.5s
# # ignored_exceptions:超时后的抛出的异常信息,默认抛出NoSuchElementExeception异常。
# # 与until()或者until_not()方法结合使用
# # WebDriverWait(driver,10).until(method,message="")
# # 调用该方法提供的驱动程序作为参数,直到返回值为True
# # WebDriverWait(driver,10).until_not(method,message="")
# # 调用该方法提供的驱动程序作为参数,直到返回值为False
# # 在设置时间(10s)内,等待后面的条件发生。如果超过设置时间未发生,则抛出异常。
# # 在等待期间,每隔一定时间(默认0.5秒),调用until或until_not里的方法,直到它返回True或False.
# """
# element = WebDriverWait(driver, 5, 0.5).until(EC.presence_of_element_located((By.ID, "TANGRAM__PSP_4__footerULoginBtn")))
# element.click()
#
# # 输入账号密码
# driver.find_element_by_id("TANGRAM__PSP_4__userName").clear()
# driver.find_element_by_id("TANGRAM__PSP_4__userName").send_keys("********")
#
# driver.find_element_by_id("TANGRAM__PSP_4__password").clear()
# driver.find_element_by_id("TANGRAM__PSP_4__password").send_keys("********")
#
# sleep(2)
#
# driver.find_element_by_id("TANGRAM__PSP_4__submit").click()
"""
企业信息门户模拟登录
"""
# driver.get("http://www.******.com/")
# element = WebDriverWait(driver, 5, 0.5).until(EC.presence_of_element_located((By.XPATH, "/html/body/div[1]/div/div/div[2]/div[1]/div[1]/div[1]/ul/li[2]/a")))
# element.click()
# sleep(1)
#
# # 输入账号密码
# user_name = driver.find_element_by_id("usernameMsg")
# user_name.clear()
# user_name.send_keys("********")
# sleep(1)
# #display: none点击输入框后变为display: inline-block,报错:could not be scrolled into view(firefox)
# js = "document.getElementById(\"passwordMsg\").style.display='block';"
# driver.execute_script(js)
# password = driver.find_element_by_id("passwordMsg")
# # print(password.is_displayed())
# password.clear()
# password.send_keys("*********")
# sleep(1)
# #获取验证码
# get_code_btn = driver.find_element_by_id("getCode1")
# get_code_btn.click()
#
# code_time = time()
#
#
# key = input('请输入验证码:')
# js = "document.getElementById(\"verifyCode\").style.display='block';"
# driver.execute_script(js)
# driver.find_element_by_id("verifyCode").send_keys(key)
#
# # sleep(2)
#
# #点击“登录”时报错:Element is not clickable at point (958, 607). Other element would receive the click:
# #解决办法:通过js来操作点击
# login_btn = driver.find_element_by_id("login")
# print(login_btn.is_enabled())
# js = 'document.getElementById("login").click();'
# driver.execute_script(js)
except Exception as e:
print("Get Element Error:%s" % repr(e))
finally:
sleep(2)
# 生成快照
driver.save_screenshot(u"selenium.png")
driver.quit()