selenium对网页进行UI自动化时经常会遇到操作系统弹框,比如上传、下载框,对这种弹框,selenium无法处理。
上传按钮有简单的input按钮,还有js、flash等复杂按钮,针对不同的按钮,我们有不同的自动化方法。
input标签直接send_keys
以网址:http://www.sahitest.com/demo/php/fileUpload.htm为示例。
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('http://sahitest.com/demo/php/fileUpload.htm')
upload = driver.find_element_by_id('file')
upload.send_keys('d:\\baidu.py') # send_keys
print upload.get_attribute('value') # check value
driver.quit()
对于input上传,直接send_keys是最简单的解决方案。
win32gui上传文件
先用Spy++
找到窗口类和名。
# -*- coding: utf-8 -*-
from selenium import webdriver
import win32gui
import win32con
import time
dr = webdriver.Firefox()
dr.get('http://sahitest.com/demo/php/fileUpload.htm')
upload = dr.find_element_by_id('file')
upload.click()
time.sleep(1)
# win32gui
dialog = win32gui.FindWindow('#32770', u'文件上传') # 对话框
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, 'd:\\baidu.py') # 往输入框输入绝对地址
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button) # 按button
print upload.get_attribute('value')
dr.quit()
其中用到几个重要API:
win32gui.FindWindow(lpClassName=None, lpWindowName=None):
自顶层窗口开始寻找匹配条件的窗口,并返回这个窗口的句柄。
lpClassName
:类名,在Spy++里能够看到
lpWindowName
:窗口名,标题栏上能看到的名字
代码示例里我们用来寻找上传窗口,你可以只用其中的一个,用classname定位容易被其他东西干扰,用windowname定位不稳定,不同的上传对话框可能window_name不同,怎么定位取决于你的情况。
win32gui.FindWindowEx(hwndParent=0, hwndChildAfter=0, lpszClass=None, lpszWindow=None)
搜索类名和窗体名匹配的窗体,并返回这个窗体的句柄。找不到就返回0。
hwndParent
:若不为0,则搜索句柄为hwndParent窗体的子窗体。
hwndChildAfter
:若不为0,则按照z-index的顺序从hwndChildAfter向后开始搜索子窗体,否则从第一个子窗体开始搜索。
lpClassName
:字符型,是窗体的类名,这个可以在Spy++里找到。
lpWindowName
:字符型,是窗口名,也就是标题栏上你能看见的那个标题。
代码示例里我们用来层层寻找输入框和寻找确定按钮
win32gui.SendMessage(hWnd, Msg, wParam, lParam)
hWnd
:整型,接收消息的窗体句柄
Msg
:整型,要发送的消息,这些消息都是windows预先定义好的,可以参见系统定义消息(System-Defined Messages)
wParam
:整型,消息的wParam参数
lParam
:整型,消息的lParam参数
代码示例里我们用来向输入框输入文件地址以及点击确定按钮
win32ui的其他函数和用法,参见文档:
https://github.com/mhammond/pywin32
SendKeys
安装:
pip install SendKeys
# -*- coding: utf-8 -*-
from selenium import webdriver
import win32gui
import win32con
import time
dr = webdriver.Firefox()
dr.get('http://sahitest.com/demo/php/fileUpload.htm')
upload = dr.find_element_by_id('file')
upload.click()
time.sleep(1)
# SendKeys
SendKeys.SendKeys('D:\\baidu.py') # 发送文件地址
SendKeys.SendKeys("{ENTER}") # 发送回车键
print upload.get_attribute('value')
dr.quit()
autoIT操作系统弹框
用autoIT可以操控很多控件。
想要参数化传入的参数,可以通过autoit
的命令行参数:
D:\> myProg.exe param1 "This is a string parameter" 99
结果:
$CmdLine[0] ; = 3
$CmdLine[1] ; = param1
$CmdLine[2] ; = "This is a string parameter"
$CmdLine[3] ; = 99
$CmdLineRaw ; = 'param1 "This is a string parameter" 99'
$CmdLine[0]
获取的是命令行参数的总数,在上例中
CmdLine[1]~
CmdLineRaw` 获取的是未拆分的所有参数,是一个长字符串,这种情况下不局限与63个参数
通过autoit的获取对象并编辑脚本:
ControlFocus("文件上传", "", "Edit1")
WinWait("[CLASS:#32770]", "", 10)
ControlSetText("文件上传" ,"", "Edit1", $CmdLine[1])
Sleep(2000)
ControlClick("文件上传", "","Button1");
通过Aut2Exe工具将脚本转成exe文件(upfile.exe)
我们先通过命令行试试,打开网页上传弹框,然后在cmd中执行该脚本:
D:\> upfile.exe "D:\1.html"
接下来就是用Python用os模块来调用该文件了:
# -*- coding: utf-8 -*-
from selenium import webdriver
import os
import time
driver = webdriver.Firefox()
driver.get('http://www.sahitest.com/demo/php/fileUpload.htm')
driver.find_element_by_id('file').click()
time.sleep(1)
# 这里可以对传参进行参数化,我们可以通过py脚本来控制所要上传的文件了
os.system('D:\\upfile.exe "D:\\1.html"')
time.sleep(3)
driver.quit()
还可以用Python版的autoIT:
https://github.com/jacexh/pyautoit
多文件上传
多文件上传就是在文件路径框里用引号括起单个路径,然后用空格隔开多个路径,并且多个文件在同一路径下。如下格式:
"D:\\a.txt" "D:\\b.txt"
# -*- coding: utf-8 -*-
from selenium import webdriver
import win32gui
import win32con
import time
dr = webdriver.Firefox()
dr.get('http://www.sucaijiayuan.com/api/demo.php?url=/demo/20150128-1')
dr.switch_to.frame('iframe') # 一定要注意frame
dr.find_element_by_class_name('filePicker').click()
time.sleep(1)
dialog = win32gui.FindWindow('#32770', None)
ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
ComboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, 'ComboBox', None)
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)
button = win32gui.FindWindowEx(dialog, 0, 'Button', None)
# 跟上面示例的代码是一样的,只是这里传入的参数不同,如果愿意可以写一个上传函数把上传功能封装起来
win32gui.SendMessage(Edit, win32con.WM_SETTEXT, 0, '"d:\\baidu.py" "d:\\upload.py" "d:\\1.html"')
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)
print dr.find_element_by_id('status_info').text
dr.quit()