一、selenium简介
Selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架。它是一款用于运行端到端功能测试的超强工具。您可以使用多个编程语言编写测试,并且 Selenium 能够在一个或多个浏览器中执行这些测试。Selenium的发展经历了三个阶段:
selenium1的时代,在运行selenium1.0程序之前,我们得先启动selenium server端,即selenium RC。RC主要包括三个部分:launcher,http proxy,selenium core。其中selenium core是被selenium server嵌入到浏览器页面中的,selenium core内部是一堆javascript函数构成,通过调用这些函数来实现对浏览器的各种操作。很显然比较繁琐,这并不是最佳自动化解决方案,于是后来有了webdriver。
selenium2 中主推的是 WebDriver ,WebDriver 是 selenium RC 的替代品,selenium2是默认支持Firefox浏览器的,这点非常方便。当然也支持其他更多浏览器,Ie和chrome浏览器需要下载驱动包,并添加到环境变量下。
selenium3 是2016年10月份左右出来的,并且现在默认安装都是selenium3了,selenium3在selenium2的基础上做了一些调整,最明显的区别就是 selenium2对Firefox的支持最高只支持46及以下版本。selenium3可以支持47以上版本,但是需要下载geckodriver.exe驱动,并添加到环境变量path下。Selenium3.0只支持Java8版本以上,如果你是用Java+Selenium开发自动化测试,那么Java JDK需要升级到Java8了。
二、appium与Selenium
appium类库封装了标准Selenium客户端类库,为用户提供所有常见的JSON格式selenium命令以及额外的移动设备控制相关的命令,如多点触控手势和屏幕朝向。appium服务端定义了官方协议的扩展,为appium用户提供了方便的接口来执行各种设备动作,例如在测试过程中安装/卸载App。这就是为什么我们需要appium特定的客户端,而不是通用的Selenium客户端。当然,appium客户端类库只是增加了一些功能,而实际上这些功能就是简单的扩展了Selenium客户端,所以他们仍然可以用来运行通用的Selenium会话。appium扩展了WebDriver的协议,没有自己重新去实现一套。这样的好处是以前的WebDriver API能够直接被继承过来,以前的Selenium(WebDriver)各种语言的binding都可以拿来就用,省去了为每种语言开发一个client的工作量。
三、Selenium3 & python
一. 安装Python3
二. 安装selenium
pip install selenium 即可自动安装selenium
三、验证selenium
cmd窗口输入如下指令(以Firefox为例)
>python
>from selenium import webdriver
>webdriver.Firefox()
如果能启动浏览器,说明环境安装OK。
四、selenium浏览器驱动的下载与调用
1、安装三大浏览器驱动driver
1)chrome的驱动chromedriver 下载地址:
https://code.google.com/p/chromedriver/downloads/list
http://chromedriver.storage.googleapis.com/index.html
https://npm.taobao.org/mirrors/chromedriver/
2)IE的驱动IEdriver 下载地址:
http://www.nuget.org/packages/Selenium.WebDriver.IEDriver/
http://selenium-release.storage.googleapis.com/index.html
3)Firefox的驱动geckodriver
selenium 3.0版本调用firefox的时候要用geckodriver.exe,selenium2无需驱动包。下载地址:
https://github.com/mozilla/geckodriver/releases/
4)其他浏览器驱动 :
selenium还支持 Android(AndroidDriver)和 iPhone(IPhoneDriver) 的移动应用测试。
https://www.npmjs.com/package/selenium-webdriver
2、selenium 驱动ie 和 chrome 、Firefox的三种种方式:
selenium驱动ie,chrome 的时候需要下载驱动 IEDriverServer、Chromedriver、geckodriver
1)把 ie驱动、chrome驱动放在相应文件夹,在程序中添加路径即可:
Chrome:
System.setProperty(“webdriver.chrome.driver”,”D:\Chromedriver.exe”);
WebDriver driver = new ChromeDriver();
IE:
System.setProperty("webdriver.ie.bin","D:\\IEDriverServer.exe");
WebDriver driver = new InternetExplorerDriver();
2)直接把 ie驱动、chrome驱动放在Python根目录或其他目录下(只要该目录添加到了系统环境变最path下面即可),然后直接驱动
3)将驱动放在各自浏览器的安装目录下,并设置用户环境变量
将chromedriver.exe 放在chrome浏览器安装目录下(同时设置用户环境变量path:C:\Users\xxxxxx\AppData\Local\Google\Chrome\Application;)
将IEDriverServer.exe 放在ie浏览器安装目录下(同时设置用户环境变量path:C:\Program Files\Internet Explorer )
调用ie浏览器时,要注意安全设置问题(internet选项--》安全 去掉“启用保护模式”的勾选)
五、selenium 调用浏览器
web自动化练习网站http://sahitest.com/demo/index.htm
调用chrome浏览器
from selenium import webdriver
driver=webdriver.Chrome() #调用chrome浏览器
driver.get('https://www.baidu.com')
print (driver.title)
driver.quit()
调用IE浏览器
from selenium import webdriver
driver=webdriver.Ie() #调用IE浏览器
driver.get('https://www.baidu.com')
print (driver.title)
driver.quit()
调用firefox浏览器
from selenium import webdriver
import time
driver=webdriver.Firefox() #调用chrome浏览器
driver.get('https://www.baidu.com')
driver.find_element_by_id("kw").send_keys("Selenium2")
driver.find_element_by_id("su").click()
time.sleep(3) # 强制等待3秒再执行下一步, 强制等待,不管你浏览器是否加载完了
driver.quit()
Selenium的基本使用
selenium下包含2个包,common和webdriver,common下仅有一个exceptions。selenium.common.exceptions所有selenium中可能发生的异常。其他操作及功能都在webdriver下。webdriver里除了common 和support ,其余的都是对应浏览器的方法/属性等。
(1)浏览器对象
Selenium支持很多浏览器包括chrome、Firefox、Edge、Safari等,各浏览器初始化对象方法:
from selenium import webdriver #browser=webdriver.Firefox() browser=webdriver.Chrome() #browser=webdriver.Edge() #browser=webdriver.Safari() print(type(browser)) #返回的是一个WebDriver对象 <class 'selenium.webdriver.chrome.webdriver.WebDriver'>
WebDriver对象的方法和属性:(browser=webdriver.Chrome()即browser的方法和属性)
back(): 在浏览器历史记录中后退一步
forward(): 在浏览器历史上前进一步
close(): 关闭当前窗口
quit():退出驱动程序并关闭每个关联的窗口
refresh():刷新当前页面
name:返回此实例的基础浏览器的名称
title:返回当前页面的标题
current_url:获取当前页面的URL
add_cookie(cookie_dict): 为当前会话添加一个cookie,为字典类型
delete_all_cookies(): 删除会话范围内的所有cookie
delete_cookie(name): 删除具有给定名称的单个cookie
get_cookie(name):按名称获取单个cookie
get_cookies():返回一组字典的cookies
execute(driver_command,params=None): 发送command执行的命令
execute_async_script(script,*args): 异步执行当前窗口或框架中的JavaScript,它不会阻塞主线程执行。
execute_script(script,*args): 同步执行当前窗口或框架中的JavaScript,用它执行js代码会阻塞主线程执行,直到js代码执行完毕
get(url):在当前浏览器会话中加载网页,一定要输入全部链接,包括“http://”,否则可能找不到
get_log(log_type):获取给定日志类型的日志
get_screenshot_as_base64():获取当前窗口的屏幕截图,作为base64编码的字符串
get_screenshot_as_file(filename):将当前窗口中的截屏保存为png图形
save_screenshot(filename):将当前窗口的屏幕截图保存为PNG图形文件
get_screenshot_as_png():获取当前窗口的屏幕截图作为二进制数据
get_window_position(windowhandle='current'):获取当前窗口的x,y位置
get_window_rect():获取窗口的x,y坐标以及当前窗口的高度和宽度
get_window_size():获取当前窗口的高度和宽度
maximize_window():最大化webdriver正在使用的当前窗口
minimize_window():最小化当前webdricer使用窗口
fullscreen_window():调用窗口管理器特定的全屏操作
set_window_rect(x=None,y=None,width=None,height=None):设置窗口的x,y坐标以及当前窗口的高度和宽度
set_window_size(width,height,windowHandle='current'):设置当前窗口的高度和宽度
set_window_position(x,y,windowHandle='current'):设置当前窗口的x,y位置
current_window_handle:返回当前窗口的句柄
window_handles:返回当前会话中所有窗口的句柄
create_web_element(element_id): 使用指定的id创建Web元素
set_page_load_timeout(time_to_wait):设置等待页面加载完成的时间
set_script_timeout(time_to_wait):设置脚本在执行期间等待的时间
desired_capabilities:返回驱动程序当前使用的所需功能
log_types:获取可用日志类型的列表
page_source:获取当前页面的源码
switch_to 将焦点切换到所有选项的对象上
switch_to.alert 返回浏览器的Alert对象,可对浏览器alert、confirm、prompt框操作
switch_to.default_content() 切到主文档
switch_to.frame(frame_reference) 切到某个frame
switch_to.parent_frame() 切到父frame,有多层frame的时候很有用
switch_to.window(window_name) 切到某个浏览器窗口
switch_to.active_element 返回当前焦点的WebElement对象,网页上当前操作的对象(也就是你网页上光标的位置),比如你鼠标点击到了一个input框,你可以在这个input框里输入信息,这时这个input框即焦点。
实现JavaScript:
from selenium import webdriver
driver=webdriver.Chrome()
driver.get('https://www.baidu.com')
driver.execute_script("alert('are you sure');") #它基本可以实现JavaScript的所有功能
from selenium import webdriver browser=webdriver.Chrome() browser.get('http://selenium-python.readthedocs.io') browser.execute_script('window.open("https://www.baidu.com");') #在标签页打开URL browser.execute_script('window.open("https://www.taobao.com");') browser.back() #后退到前一个页面 browser.set_page_load_timeout(5) browser.forward() #前进到下一个页面 print(browser.name) print(browser.title) print(browser.current_url) print(browser.current_window_handle) print(browser.get_cookies()) print(type(browser))
获取页面截图:
from selenium import webdriver driver=webdriver.Chrome() driver.get('http://www.python.org') driver.save_screenshot('screenshot.png') #保持页面截图到当前路径 driver.quit()
将页面滚动到底部:
from selenium import webdriver driver=webdriver.Chrome() driver.get('http://www.python.org') #通过js中的window对象的scrollTo方法,将窗口位置滚动到指定位置,document.body.scrollHeight返回整个body的高度,所以页面将滚动到页面底部 driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")
cookies操作:
#!/usr/bin/env python # -*- coding: utf-8 -*- from selenium import webdriver driver=webdriver.Chrome() driver.get('https://www.baidu.com') print(driver.get_cookies()) #获取所有cookies driver.add_cookie({'name':'name','domain':'www.baidu.com','value':'germey'}) #添加cookie print(driver.get_cookies()) driver.delete_all_cookies() print(driver.get_cookies())
(2)定位元素
webdriver 提供了八种元素定位方法:id、name、class name、tag name、link text、partial link text、xpath、css selector
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector
要查找多个元素(这些方法将返回一个列表):
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
#!/usr/bin/env python # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By
driver=webdriver.Chrome() driver.get('http://selenium-python.readthedocs.io/locating-elements.html#locating-elements')
data=driver.find_elements(By.CLASS_NAME,'simple') #获取多个 data=driver.find_element(By.CLASS_NAME,'simple') #获取一个 #driver.find_element(By.ID,'IDname') #获取ID标签定位元素 #driver.find_element(By.CSS_SELECTOR,'cssname')#CSS选择器定位元素 #driver.find_element(By.LINK_TEXT,'linktext') #链接文本定位元素 #driver.find_element(By.PARTIAL_LINK_TEXT,'linktext') #部分链接文件定位元素 #driver.find_element(By.NAME,'name') #属性名定位元素 #driver.find_element(By.TAG_NAME,'tagname') #标签名定位元素 print(data.text) #打印元素文本内容
通过Id定位:
当您知道元素的id属性时使用此选项。使用此策略,将返回id属性值与该位置匹配的第一个元素,如使用find_elements_by_id将返回多个匹配的元素。如果没有元素具有匹配的id 属性,NoSuchElementException则会引发
driver.find_element_by_id('kw1') driver.find_elements_by_id('kw1')
按名称定位:
当您知道元素的name属性时,请使用此选项。使用此策略,将返回名称属性值与位置匹配的第一个元素,如使用find_elements_by_name将返回多个匹配的元素。如果没有元素具有匹配的name 属性,NoSuchElementException则将引发
driver.find_element_by_name('wd') driver.find_elements_by_name('wd')
按标签名称定位元素:
如果要按标签名称查找元素,请使用此选项。使用此策略,将返回具有给定标记名称的第一个元素。如果没有元素具有匹配的标记名称则将引发NoSuchElementException异常。tag name 获取的是标签的名字,在一个页面上往往会有很多标签名相同的元素,这并不是说 tag name 方法就毫无用武之处,在定位一组元素的时候我们往往需要 tag name 方法来帮忙。
driver.find_element_by_tag_name('input')
按类名定位元素:
如果要按类属性名称定位元素,请使用此选项。使用此策略,将返回具有匹配类属性名称的第一个元素。如果没有元素具有匹配的类属性名称,NoSuchElementException则将引发
driver.find_element_by_class_name('s_ipt')
通过链接文本查找超链接:
当操作的元素是一个文字链接, 那么我们可以通过link text 或 partial link text 进行元素定位。将返回链接文本值与位置匹配的第一个元素。如果没有元素具有匹配的链接文本属性,NoSuchElementException则将引发。当一个文字连接很长时,我们可以只取其中的一部分,只要取的部分可以唯一标识元素。一般一个页面上不会出现相同的文件链接,通过文字链接来定位元素也是一种简单有效的定位方式。
driver.find_element_by_link_text('继续) #通过链接文本定位到元素
driver.find_element_by_partial_link_text('继续) #通过链接文本定位到元素
通过XPath定位:
XPath是用于在XML文档中定位节点的语言。由于HTML可以是XML的实现,因此可以利用XPath来定位其Web应用程序中的元素。XPath扩展了通过id或name属性定位的方法,并打开了各种新的可能性,例如在页面上查找第三个复选框。使用XPath的主要原因之一是当您没有适合您要查找的元素的id或name属性时。您可以使用XPath以绝对术语或相对于具有id或name属性的元素来定位元素。XPath定位器也可以用来通过id和name之外的属性指定元素。
绝对路径定位(定位最后一个元素):
find_element_by_xpath("/html/body/div[2]/form/span/input")
相对路径定位(定位最后一个元素):
#通过自身的 id 属性定位
find_element_by_xpath("//input[@id=’input’]")
#通过上一级目录的 id 属性定位
find_element_by_xpath("//span[@id=’input-container’]/input")
#通过上三级目录的 id 属性定位
find_element_by_xpath("//div[@id=’hd’]/form/span/input")
#通过上三级目录的 name属性定位
find_element_by_xpath("//div[@name=’q’]/form/span/input")
当我们所要定位的元素很难找到合适的方式时,可以通这种绝对路径的方式位,缺点是当元素在很多级目录下时,我们不得不要写很长的路径,而且这种方式难以阅读和维护。 XPath 的定位方式非常灵活和强大的,而且 XPath 可以做布尔逻辑运算,例如://div[@id=’hd’ or @name=’q’]。
driver.find_element_by_xpath("//from[1]") #查看第一个表单元素
driver.dind_element_by_xpath("//from[@id='loginform']") #查找id为loinform的表单元素
xpath定位的缺点:
1、性能差,定位元素的性能要比其它大多数方式差;
2、不够健壮,XPath 会随着页面元素布局的改变而改变;
3、兼容性不好,在不同的浏览器下对 XPath 的实现是不一样的。
通过CSS选择器定位元素:
CSS被用来描述 HTML 和 XML 文档的表现。CSS 使用选择器来为页面元素绑定属性。这些选择器可以被 selenium 用作定位。将返回具有匹配的CSS选择器的第一个元素。CSS 可以比较灵活选择控件的任意属性,一般情况下定位速度要比 XPath 快,但对于初学者来说比较难以学习使用。如果没有元素具有匹配的CSS选择器,NoSuchElementException则会引发
driver.find_element_by_css_selector('p.content')
XPath、CSS定位速查表
CSS定位语法比 XPath 更为简洁,定位方式更多灵活多样;不过对 CSS 理解起来要比 XPath较难;但不管是从性能还是定位更复杂的元素上,CSS 优于XPath,所以更推荐使用 CSS定位页面元素。
(3)元素对象(element)
当我们通过上面 的方法定位到元素后返回的对象称为web元素对象,我们可以对元素对象再进行交互或继续查找等操作
# -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.chrome.options import Options
opt=Options() opt.add_argument('headless') driver=webdriver.Chrome(chrome_options=opt) driver.get('http://selenium-python.readthedocs.io/api.html') element=driver.find_element_by_id('remote-webdriver-webelement')
print(element)
print(type(element))
element(id=remote-webdriver-webelement)的方法和属性包括:
clear() :清除文本元素
click() :单击元素按钮
get_attribute(name) :获取元素的给定属性的属性值
get_property(name) :获取元素的给定属性
is_displayed() :判断元素是否存在
is_enable() :判断元素是否被启用
is_selected() :返回元素是否被选中
screenshot(filename) :将当前元素的屏幕截图保存到文件
send_keys() #发送元素值,例如获取的搜素框后向其中输入搜素内容
submit() :提交表单
value_of_css_property() :CSS属性的值
id :selenium使用的内部ID
location :元素在可渲染画布中的位置
location_once_scrolled_into_view :发现元素在屏幕视图中的位置
rect :返回包含元素大小和位置的字典
screenshot_as_base64 :获取当前元素的截屏,作为base64编码的字符串
size :获取元素的大小
tag_name :获取元素的tagName属性
text :获取元素的文本
与页面交互,实现输出文本搜索功能,并打印搜索结果源码:
#!/usr/bin/env python # -*- coding: utf-8 -*- from selenium import webdriver driver=webdriver.Chrome() driver.get('http://www.cnblogs.com/zhangxinqi/') element=driver.find_element_by_id('q') #获取输入框元素 element.send_keys('python3之requests') #发送元素 button=driver.find_element_by_id('btnZzk') #获取搜索按钮 button.click() #发送搜索动作 data=driver.page_source print(driver.current_url) #打印URL print(data) print(type(element)) driver.close()
(4)动作链
selenium.webdriver.common.action_chains.ActionChains(driver)
在上面的实例中我们针对的是某个节点元素的操作,如果要对没有特定元素的对象操作如鼠标拖拽、键盘按键等,这些动作就称为动作链,selenium使用ActionChains()类来实现鼠标移动,鼠标按钮操作,按键操作、上下文菜单交互,悬停和拖放等
click(on_element=None) ——单击鼠标左键
click_and_hold(on_element=None) ——点击鼠标左键,不松开
context_click(on_element=None) ——点击鼠标右键
double_click(on_element=None) ——双击鼠标左键
drag_and_drop(source, target) ——拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开
key_down(value, element=None) ——按下某个键盘上的键
key_up(value, element=None) ——松开某个键
move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标
move_to_element(to_element) ——鼠标移动到某个元素
move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素(左上角坐标)多少距离的位置
perform() ——执行链中的所有动作
release(on_element=None) ——在某个元素位置松开鼠标左键
send_keys(*keys_to_send) ——发送某个键到当前焦点的元素
send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素
将元素拖拽到目标位置:
from selenium.webdriver import ActionChains
element = driver.find_element_by_name("source") target = driver.find_element_by_name("target") action = ActionChains(driver) action.drag_and_drop(element, target).perform()
执行鼠标操作的流程:
from selenium.webdriver import ActionChains
menu = driver.find_element_by_css_selector(".nav") #获取element对象
submenu = driver.find_element_by_css_selector(".nav #submenu1") #获取点击对象
actions = ActionChains(driver) #创建鼠标对象
actions.move_to_element(menu) #移动鼠标到对象
actions.click(submenu) #点击对象
actions.perform() #执行操作
(5)弹出对话框
selenium.webdriver.common.alert.Alert(driver)
首先区别下alert、window和伪装对话框:
alert,浏览器弹出框,一般是用来确认某些操作、输入简单的text或用户名、密码等,根据浏览器的不同,弹出框的样式也不一样,不过都是很简单的一个小框。在firebug中是无法获取到该框的元素的,也就是说alert是不属于网页DOM树的。如下图所示:
window,浏览器窗口,点击一个链接之后可能会打开一个新的浏览器窗口,跟之前的窗口是平行关系(alert跟窗口是父子关系,或者叫从属关系,alert必须依托于某一个窗口),有自己的地址栏、最大化、最小化按钮等。这个很容易分辨。
div伪装对话框,是通过网页元素伪装成对话框,这种对话框一般比较花哨,内容比较多,而且跟浏览器一看就不是一套,在网页中用firebug能够获取到其的元素,如下图:
Alert内置支持处理弹窗对话框的方法:
accept() :确认弹窗,用法:Alert(driver).appept()
authenticate(username,password) :将用户名和密码发送到authenticated对话框,隐含点击确定,用法driver.switch_to.alert.authenticate('username','password')
dismiss() :取消确认
send_keys(keysToSend) :将密钥发送到警报,keysToSend为要发送的文本
text :获取Alert的文本
import time from selenium import webdriver from selenium.webdriver.common.alert import Alert
driver=webdriver.Chrome() driver.get('https://www.baidu.com') driver.execute_script("alert('确定');") #弹出窗口 time.sleep(2) print(driver.switch_to.alert.text) #输出alert文本 alert=Alert(driver).accept() #自动点击确定窗口
3.window操作
window类似于alert,不过与原window是平行的,所以只需要切换到新的window上便可以操作该window的元素。driver.switch_to.window(window_handle)
而window是通过window句柄handle来定位的。而selenium提供了两个属性方法来查询window句柄:driver.current_window_handle 和driver.window_handles
用以上两个属性获取到当前窗口以及所有窗口的句柄,就可以切换到其他的window了。
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.alert import Alert
driver = webdriver.Firefox()
driver.maximize_window()
driver.get('http://sahitest.com/demo/index.htm')
current_window = driver.current_window_handle # 获取当前窗口handle name
driver.find_element_by_link_text('Window Open Test With Title').click()
all_windows = driver.window_handles # 获取所有窗口handle name
# 切换window,如果window不是当前window,则切换到该window
for window in all_windows:
if window != current_window:
driver.switch_to.window(window)
print driver.title # 打印该页面title
driver.close()
driver.switch_to.window(current_window) # 关闭新窗口后切回原窗口,在这里不切回原窗口,是无法操作原窗口元素的,即使你关闭了新窗口
print driver.title # 打印原页面title
driver.quit()
4.div类对话框
div类对话框是普通的网页元素,通过正常的find_element就可以定位并进行操作。不在这里进行详述。注意设置一定的等待时间,以免还未加载出来便进行下一步操作,造成NoSuchElementException报错。
(6)键盘操作
selenium.webdriver.common.keys.Keys
selenium提供一个keys包来模拟所有的按键操作,下面是一些常用的按键操作:
- 回车键:Keys.ENTER
- 删除键:Keys.BACK_SPACE
- 空格键:Keys.SPACE
- 制表键:Keys.TAB
- 回退键:Keys.ESCAPE
- 刷新键:Keys.F5
- 全选(ctrl+A):send_keys(Keys.CONTROL,'a') #组合键需要用send_keys方法操作
- 复制(ctrl+C):send_keys(Keys.CONTROL,'c')
- 剪切(ctrl+X):send_keys(Keys.CONTROL,'x')
- 粘贴(ctrl+V):send_keys(Keys.CONTROL,'v')
#!/usr/bin/env python # -*- coding: utf-8 -*-
#在python的pypi页面,通过点击页面下载selenium源码包: import requests from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains
driver=webdriver.Chrome() driver.get('https://pypi.org/') element=driver.find_element_by_id('search') #获取输入框 element.send_keys('selenium') #搜索selenium包 element.send_keys(Keys.ENTER) #按回车键
#element_a=driver.find_element_by_link_text('selenium') #定位selenium包链接
element_a = driver.find_element_by_xpath('//*[@id="content"]/section/div/div[2]/form/section[2]/ul/li[1]/a/h3/span[1]')#定位selenium包链接
ActionChains(driver).move_to_element(element_a).click(element_a).perform() #按左键点击链接执行
element_down=driver.find_element_by_link_text('Download files') #定位下载链接 ActionChains(driver).move_to_element(element_down).click(element_down).perform() #按左键点击链接 element_selenium=driver.find_element_by_link_text('selenium-3.13.0.tar.gz') #定位元素selenium下载包链接 data=element_selenium.get_attribute('href') #获取链接地址 with open('selenium-3.13.0.tar.gz','wb') as f: source=requests.get(data).content #请求下载链接地址获取二进制包数据 f.write(source) #写入数据 f.close() driver.quit()
(7)延时等待
目前大多数Web应用程序都在使用AJAX技术。当浏览器加载页面时,该页面中的元素可能以不同的时间间隔加载。这使定位元素变得困难:如果DOM中尚未存在元素,则locate函数将引发ElementNotVisibleException异常。使用等待,我们可以解决这个问题。Selenium Webdriver提供两种类型的等待: 隐式和显式。
显式等待使WebDriver等待某个条件发生,然后再继续执行。隐式等待在尝试查找元素时,会使WebDriver轮询DOM一段时间。
显示等待:
显性等待WebDriverWait,配合该类的until()和until_not()方法,就能够根据判断条件而进行灵活地等待了。它主要的意思就是:程序每隔xx秒看一眼,如果条件成立了,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出TimeoutException。
until
method: 在等待期间,每隔一段时间(__init__中的poll_frequency)调用这个传入的方法,直到返回值不是False
message: 如果超时,抛出TimeoutException,将message传入异常
until_not
与until相反,until是当某元素出现或什么条件成立则继续执行,
until_not是当某元素消失或什么条件不成立则继续执行,参数也相同,不再赘述。
常用等待条件:
- title_is :标题是某内容
- title_contains :标题包含某内容
- presence_of_element_located :节点加载出来,传入定位元组,如(By.ID, 'p')
- presence_of_all_elements_located :所有节点加载出来
- visibility_of_element_located :节点可见,传入定位元组
- visibility_of :可见,传入节点对象
- text_to_be_present_in_element :某个节点文本包含某文字
- text_to_be_present_in_element_value :某个节点值包含某文字
- invisibility_of_element_located :节点不可见
- frame_to_be_available_and_switch_to_it :加载并切换
- element_to_be_clickable :节点可点击
- staleness_of :判断一个节点是否仍在DOM,可判断页面是否已经刷新
- element_to_be_selected :节点可选择,传节点对象
- element_located_to_be_selected :节点可选择,传入定位元组
- element_selection_state_to_be :传入节点对象以及状态,相等返回True,否则返回False
- element_located_selection_state_to_be :传入定位元组以及状态,相等返回True,否则返回False
- alert_is_present :是否出现警告
# -*- coding: utf-8 -*- 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://www.taobao.com/') wait=WebDriverWait(driver,3) #设置监听driver等待时间3秒 input=wait.until(EC.presence_of_element_located((By.ID,'q'))) #设置等待条件为id为q的元素加载完成 button=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search'))) #设置等待条件为class名为btn-search的元素加载完成 print(input,button)
隐式等待:
Selenium在DOM中没有找到节点,如果使用了隐式等待,Selenium将会进行等待,当超出设定时间后,则抛出找不到节点的异常。即当要查找的节点没有立即出现的时候,隐式等待将等待一段时间再查找DOM,默认的时间是0。调用driver的implicitly_wait()方法实现隐式等待:
from selenium import webdriver driver = webdriver.Chrome() driver.implicitly_wait(10) #隐式等待设置等待时间为10s driver.get("http://somedomain/url_that_delays_loading") myDynamicElement = driver.find_element_by_id("myDynamicElement")
(8)异常处理
所有webdriver代码中发生的异常:
selenium.common.exceptions.WebDriverException :webdriver基本异常
selenium.common.exceptions.UnknownMethodException :请求的命名与URL匹配但该URL方法不匹配
selenium.common.exceptions.UnexpectedTagNameException :当支持类没有获得预期的Web元素时抛出
selenium.common.exceptions.UnexpectedAlertPresentException :出现意外警报时抛出,通常在预期模式阻止webdriver表单执行任何更多命令时引发
selenium.common.exceptions.UnableToSetCookieException :当驱动程序无法设置cookie时抛出
selenium.common.exceptions.TimeoutException :当命令没有在足够的时间内完成时抛出
selenium.common.exceptions.StaleElementReferenceException :当对元素的引用现在“陈旧”时抛出,陈旧意味着该元素不再出现在页面的DOM上
selenium.common.exceptions.SessionNotCreatedException :无法创建新会话
selenium.common.exceptions.ScreenshotException :屏幕截图错误异常
selenium.common.exceptions.NoSuchWindowException :当不存在要切换的窗口目标时抛出
selenium.common.exceptions.NoSuchElementException :无法找到元素时抛出
selenium.common.exceptions.NoSuchCookieException :在当前浏览上下文的活动文档的关联cookie中找不到与给定路径名匹配的cookie
selenium.common.exceptions.NoSuchAttributeException :无法找到元素的属性时抛出
selenium.common.exceptions.JavascriptException :执行用户提供的JavaScript时发生错误
#!/usr/bin/env python # -*- coding: utf-8 -*- from selenium import webdriver from selenium.common.exceptions import NoSuchElementException driver=webdriver.Chrome() driver.get('https://www.baidu.com') try: element=driver.find_element_by_id('test') print(element) except NoSuchElementException as e: print('元素不存在:',e)
(9)实例:抓取淘宝页面商品信息
#!/usr/bin/env python #coding:utf-8 import re import pymongo from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from urllib.parse import quote from selenium.common.exceptions import WebDriverException from pyquery import PyQuery as pq #链接mongodb数据库 client=pymongo.MongoClient(host='localhost',port=27017) db=client['taobao'] #定义无头chrome opt=webdriver.ChromeOptions() opt.add_argument('--headless') driver=webdriver.Chrome(chrome_options=opt) #定义页面等待时间 wait=WebDriverWait(driver,10) #定义搜索商品名 uname='iPad' #搜索商品 def search(): try: url = 'https://s.taobao.com/search?q=' + quote(uname) driver.get(url) total=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-pager > div > div > div > div.total'))) except TimeoutException: return search() return total.text #实现翻页商品 def next_page(page): print('正在抓取第{}'.format(page)) try: if page >= 1: input=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-pager > div > div > div > div.form > input'))) submit=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit'))) input.clear() input.send_keys(page) submit.click() wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,'#mainsrp-pager > div > div > div > ul > li.item.active > span'),str(page))) wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-itemlist .items .item'))) get_products() except TimeoutException: next_page(page) except WebDriverException as e: print('index_page:',e) #解析商品信息 def get_products(): #print('开始解析页面...') html = driver.page_source doc = pq(html, parser='html') items = doc('#mainsrp-itemlist .items .item').items() for i in items: product = { 'image': 'https:' + i.find('.pic .img').attr('data-src'), 'price': i.find('.price').text(), 'deal': i.find('.deal-cnt').text(), 'title': i.find('.title').text(), 'shop': i.find('.shop').text(), 'location': i.find('.location').text() } #print(product) save_to_mongo(product) #保存到mongodb def save_to_mongo(result): try: if db['collection_taobao'].insert(result): print('保存到mongodb成功!',result) except Exception: print('保存到mongodb失败',result) #主函数调用 def main(): try: total=search() total=int(re.compile('(\d+)').search(total).group(1)) for i in range(1,total+1): next_page(i) finally: driver.quit() #执行函数入口 if __name__ == '__main__': main()
晋级
学习怎么用断言来添加检查点。
你要学习怎么将代码中公共的部分抽出来,把测试和业务分离开,学习PageObject思想。
学习的是Python的doctest和unittest。用testsuite来组织你的测试用例,学习HTMLTestRunner怎么输出报告
selenium文件上传所有方法整理总结
文件上传是所有UI自动化测试都要面对的一个头疼问题,今天博主在这里给大家分享下自己处理文件上传的经验,希望能够帮助到广大被文件上传坑住的seleniumer。
首先,我们要区分出上传按钮的种类,大体上可以分为两种,一种是input框;一种是标签非input,这种就比较复杂一般通过js、flash等实现。
1.input标签
众所周知,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()
结果:
baidu.py
很明显,对于input上传,直接send_keys是最简单的解决方案。
2.非input型上传
这种上传千奇百怪,有用a标签的,有用div的,有用button的,有用object的,我们没有办法通过直接在网页上处理掉这些上传,唯一的办法就是打开OS弹框,去处理弹框。OS弹框涉及的层面已经不是selenium能解决的了,怎么办?很简单,用OS层面的操作去处理呗,到这里我们基本找到了问题的处理方法。
大体上有以下几种解决方案:
autoIT,借助外力,我们去调用其生成的au3或exe文件。
Python pywin32库,识别对话框句柄,进而操作
SendKeys库
keybd_event,跟3类似,不过是模拟按键,ctrl+a,ctrl+c, ctrl+v…
1. autoIT
selenium对网页进行UI自动化时经常会遇到OS弹框,比如上传、下载框,对这种弹框,selenium无法处理,常常我们会选择autoit这个工具。
示例网址:http://www.sahitest.com/demo/php/fileUpload.htm
通过autoit的获取对象并编辑脚本(autoit v2版本使用的是.aut后缀的扩展名,而v3版本使用的是.au3后缀):
ControlFocus("文件上传", "", "Edit1")
WinWait("[CLASS:#32770]", "", 10)
ControlSetText("文件上传" ,"", "Edit1", $CmdLine[1])
Sleep(2000)
ControlClick("文件上传", "","Button1");
可以通过Aut2Exe工具将脚本转成exe文件(upfile.exe)。接下来就是用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)
os.system('D:\\upfile.exe "D:\\1.html"') # 这里可以对传参进行参数化,我们可以通过py脚本来控制所要上传的文件了
time.sleep(3)
driver.quit()
这里只是个示例,实际上对于这种input标签,我们直接send_keys就可以了。
2.win32gui
示例网址:http://www.sahitest.com/demo/php/fileUpload.htm
# -*- 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()
结果:
baidu.py
在这里你需要一个非常重要的小工具:Spy++。而且你得安装pywin32的库,你可以到这里找到对应你Python版本的库,注意32位还是64位一定要和你安装的Python版本对应。
3.SendKeys
首先要安装SendKeys库。示例网址:http://www.sahitest.com/demo/php/fileUpload.htm
# -*- 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()
结果:
baidu.py
通过SendKeys库可以直接向焦点里输入信息,不过要注意在打开窗口是略微加一点等待时间,否则容易第一个字母send不进去(或者你可以在地址之前加一个无用字符),不过我觉得这种方法很不稳定,不推荐。
4.keybd_event
win32api提供了一个keybd_event()方法模拟按键,不过此方法比较麻烦,也不稳定,所以很不推荐
3.多文件上传
还有一种情况值得我们考虑,那就是多文件上传。如何上传多个文件,当然我们还是往输入框里输入文件路径,所以唯一要搞清楚的就是多文件上传时,文件路径是怎么写的。
多文件上传就是在文件路径框里用引号括起单个路径,然后用逗号隔开多个路径,就是这么简单,例如: “D:\a.txt” “D:\b.txt” 但需要注意的是:只有多个文件在同一路径下,才能这样用,否则是会失败的(下面的写法是不可以的): “C:\a.txt” “D:\b.txt”
示例网址:http://www.sucaijiayuan.com/api/demo.php?url=/demo/20150128-1
# -*- 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()
结果:
选中3张文件,共1.17KB。
可见,多文件上传并没有那么复杂,也很简单,唯一的区别就是输入的参数不同而已。autoIT也可以实现,有兴趣可以自己试试。
selenium搞定checkbox、radiobox
网页上有时候遇到checkbox和radio,一般情况下这两种都是input标签,我们可以通过直接点击或者发送空格的方式达到选中或者反选的目的。
#用selenium选中其中的checkbox(1、2)和radio1->radio2
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
driver = webdriver.Firefox()
driver.maximize_window()
driver.get('file:///D:/checkboxandradio.html')
# checkbox
driver.find_element_by_xpath('//input[@value="cv1"]').click() # click
driver.find_element_by_xpath('//input[@value="cv2"]').send_keys(Keys.SPACE) # send space
# radio
driver.find_element_by_xpath('//input[@value="rv1"]').send_keys(Keys.SPACE) # send space
sleep(1)
driver.find_element_by_xpath('//input[@value="rv2"]').click() # click
sleep(1)
driver.quit()
如果想要检查某个框是否被选中,我们可以通过element.is_selected()来实现。当然,选中和判断是否选中还有其他的方法,如模拟鼠标点击、用js选中、修改标签属性选中;用js、jQuery判断是否选中、用标签属性判断是否选中,不过针对大部分情况,以上方法足够用了。
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
driver = webdriver.Firefox()
driver.maximize_window()
driver.get('file:///D:/checkboxandradio.html')
# checkbox
driver.find_element_by_xpath('//input[@value="cv1"]').click() # click
driver.find_element_by_xpath('//input[@value="cv2"]').send_keys(Keys.SPACE) # send space
if driver.find_element_by_xpath('//input[@value="cv2"]').is_selected():
print 'selected!'
else:
print 'not yet!'
# radio
driver.find_element_by_xpath('//input[@value="rv1"]').send_keys(Keys.SPACE) # send space
sleep(1)
driver.find_element_by_xpath('//input[@value="rv2"]').click() # click
if driver.find_element_by_xpath('//input[@value="rv1"]').is_selected():
print 'selected!'
else:
print 'not yet!'
sleep(1)
driver.quit()
selenium下拉选择框Select
我们通常会遇到两种下拉框,一种使用的是html的标签select,另一种是使用input标签做的假下拉框。
后者我们通常的处理方式与其他的元素类似,点击或使用JS等。而对于前者,selenium给了有力的支持,就是Select类。
1.导入(import)
from selenium.webdriver.support.ui import Select
# from selenium.webdriver.support.select import Se# 或者直接从select导入
这两种方法没有本质的区别,你如果去看ui库,你会发现,它也只是把select import进去。
2.选择(select)
Select类提供了三种选择某一选项的方法:
select_by_index(index) ——通过选项的顺序,第一个为 0
select_by_value(value) ——通过value属性
select_by_visible_text(text) ——通过选项可见文本
针对于示例网站中的第一个select框,我们可以这样定位:
from selenium import webdriverd
from selenium.webdriver.support.ui import Select
driver = webdriver.Firefox()
driver.get('http://sahitest.com/demo/selectTest.htm')
s1 = Select(driver.find_element_by_id('s1Id')) # 实例化Select
s1.select_by_index(1) # 选择第二项选项:o1
s1.select_by_value("o2") # 选择value="o2"的项
s1.select_by_visible_text("o3") # 选择text="o3"的值,即在下拉时我们可以看到的文本
driver.quit()
注意:
index从 0 开始
value是option标签的一个属性值,并不是显示在下拉框中的值
visible_text是在option标签中间的值,是显示在下拉框的值
3.反选(deselect)
Select提供了四个方法给我们取消原来的选择:
deselect_by_index(index)
deselect_by_value(value)
deselect_by_visible_text(text)
deselect_all() #全部取消
4.选项(options)
当我们选择了选项之后,想要看看选择的是哪项,所选的是否是我想选的,Select为你提供了相应的方法(或者应该说是属性了),他们别返回这个select元素所有的options、所有被选中的options以及第一个被选中的option:
options
all_selected_options
first_selected_option
1 想查看一个select所有的选项
s1 = Select(driver.find_element_by_id('s1Id'))
for select in s1.options:
print select.text
结果:
o1
o2
o3
一共四项,第一项为空字符串。
selenium之Element not found in the cache
我循环去点击一列链接,但是只能点到第一个,第二个就失败了,为什么?”。原因就在这里:你点击第二个时已经是新页面,当然找不到之前页面的元素。这时,他会问“可是明明元素就在那里,没有变,甚至我是回退回来的,页面都没有变,怎么会说是新页面?”。这个就需要你明白页面长得一样不代表就是同一张页面,就像两个人长得一样不一定是同一个人,他们的身份证号不同。页面,甚至页面上的元素都是有自己的身份证号(id)的。
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('http://www.baidu.com')
print driver.find_element_by_id('kw') # kw before refresh
driver.refresh() # refresh
print driver.find_element_by_id('kw') # kw after refresh
driver.quit()
结果:
<selenium.webdriver.remote.webelement.WebElement (session="6c251157-6d81-435c-9100-97696a46ab9c", element="{f74ae41d-a557-4d5c-9029-3c122e3d0744}")>
<selenium.webdriver.remote.webelement.WebElement (session="6c251157-6d81-435c-9100-97696a46ab9c", element="{d7bd4320-31f2-4708-824f-f1a8dba3e79b}")>
我们发现,仅仅是刷新了一下页面,两次的element id是不同的,也就是说这是两个不同的元素,自然会因为找不到而报错:不仅仅这一种情况,如果你执行以下的操作,都有可能找错人:
refresh,不论你是主动刷新还是页面自动刷新
back,已经跳转到了其他页面,然后你用driver.back()跳回来,这也是一张新的页面了
跳转到了新的页面,但这张新页面上有一些元素跟之前页面是长得一样的,这也是一张新的页面了。比如:一排分页按钮,你点击下一页跳转到了第二页,想要还用原来的元素操作到下一页,那也是不可能的了。除此之外可能还有其他的原因,总之你看到这类型长得差不多,但是对页面有了操作的情况,就应该想想这种可能性了。
那遇到这种情况该怎么办?很简单:只要刷新页面之后重新获取元素就行,不要提前获取一组元素,然后去循环操作每一个元素,这种情况还是获取元素的个数,然后在循环中获取相应位置的元素,在用的时候才去获取,这样你就获取到最新的id了,也不会出现找错人的尴尬了。总之一句话,遇到页面有变化的情况,不要去循环元素,去循环个数或者定位方式,在循环中获取元素。
selenium之 定位以及切换frame
frameset不用切,frame需层层切!在用selenium定位页面元素的时候会遇到定位不到的问题,明明元素就在那儿,用firebug也可以看到,就是定位不到,这种情况很有可能是frame在搞鬼
frame标签有frameset、frame、iframe三种,frameset跟其他普通标签没有区别,不会影响到正常的定位,而frame与iframe对selenium定位而言是一样的,selenium有一组方法对frame进行操作。
1.怎么切到frame中
selenium提供了switch_to.frame(reference)方法来切换frame。reference是传入的参数,用来定位frame,可以传入id、name、index以及selenium的WebElement对象,假设有如下HTML代码 index.html:
<html lang="en">
<head>
<title>FrameTest</title>
</head>
<body>
<iframe src="a.html" id="frame1" name="myframe"></iframe>
</body>
</html>
想要定位其中的iframe并切进去,可以通过如下代码:
from selenium import webdriver
driver = webdriver.Firefox()
driver.switch_to.frame(0) # 1.用frame的index来定位,第一个是0
# driver.switch_to.frame("frame1") # 2.用id来定位
# driver.switch_to.frame("myframe") # 3.用name来定位
# driver.switch_to.frame(driver.find_element_by_tag_name("iframe")) # 4.用WebElement对象来定位
通常采用id和name就能够解决绝大多数问题。但有时候frame并无这两项属性,则可以用index和WebElement来定位:
index从0开始,传入整型参数即判定为用index定位,传入str参数则判定为用id/name定位
WebElement对象,即用find_element系列方法所取得的对象,我们可以用tag_name、xpath等来定位frame对象
<iframe src="myframetest.html" />
用xpath定位,传入WebElement对象:
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[contains(@src,'myframe')]"))
2.从frame中切回主文档
切到frame中之后,我们便不能继续操作主文档的元素,这时如果想操作主文档内容,则需切回主文档。
driver.switch_to.default_content()
3.嵌套frame的操作
有时候会遇到嵌套的frame,如下:
<html>
<iframe id="frame1">
<iframe id="frame2" / >
</iframe>
</html>
1.从主文档切到frame2,一层层切进去
driver.switch_to.frame("frame1")
driver.switch_to.frame("frame2")
2.从frame2再切回frame1,这里selenium给我们提供了一个方法能够从子frame切回到父frame,而不用我们切回主文档再切进来。
driver.switch_to.parent_frame() # 如果当前已是主文档,则无效果
有了parent_frame()这个相当于后退的方法,我们可以随意切换不同的frame,随意的跳来跳去了。
selenium之 时间日期控件的处理
时间日期控件各种各样真要一个个想着怎么去选择,简直是非人类干的事!那么该怎么办?其实很简单,我们不去搞时间日期空间,我们把它当成一个普通的input框处理就好了!但是,很多此类型input框都是禁止手动输入的, 很简单,用js把禁止输入的readonly属性干掉就好了。
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get('http://www.sucaijiayuan.com/api/demo.php?url=/demo/20141108-1/')
driver.switch_to.frame('iframe')
# js = "document.getElementById('txtBeginDate').removeAttribute('readonly')" # 1.原生js,移除属性
# js = "$('input[id=txtBeginDate]').removeAttr('readonly')" # 2.jQuery,移除属性
# js = "$('input[id=txtBeginDate]').attr('readonly',false)" # 3.jQuery,设置为false
js = "$('input[id=txtBeginDate]').attr('readonly','')" # 4.jQuery,设置为空(同3)
driver.execute_script(js)
driver.find_element_by_id('txtBeginDate').send_keys('2016-08-24')
sleep(2)
print driver.find_element_by_id('txtBeginDate').get_attribute('value')
driver.quit()
当然你去选也没有问题,就是一堆div、ul、li,但是对于日期时间控件,认真你就输了,绕过去才是真理,就像验证码一样,这个东西去深究,一点意义也没有嘛
selenium之 如何控制网页内嵌div中滚动条的滚动
selenium控制网页div中滚动条的滚动,其实这个问题很简单,用JS很简单就可以实现。
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div.scroll
{
background-color:#00FFFF;
width:100px;
height:100px;
overflow:auto;
}
</style>
</head>
<body>
<p>overflow:scroll</p>
<div class="scroll">You can use the overflow property when you want to have better control of the layout. The default value is visible.aaaaaaaaaaaaaaaaaaaaaaaaaaaa</div>
</body>
</html>
用JS来控制里面的滚动条滚动:
from selenium import webdriver
dr=webdriver.Firefox()
dr.get('file:///D:/1.html')
js='document.getElementsByClassName("scroll")[0].scrollTop=10000' # 就是这么简单,修改这个元素的scrollTop就可以
#js='document.getElementsByClassName("scroll")[0].scrollHeight’ # 获取滚动条高度
#js='document.getElementsByClassName("scroll")[0].scrollWidth’ # 获取横向滚动条宽度
#js='document.getElementsByClassName("scroll")[0].scrollLeft=xxx’ # 控制横向滚动条位置
dr.execute_script(js)
selenium动态id、class怎么定位
没有打开新页面、没有alert、没有frame、加了等待时间,但是还是定位不到元素?很有可能是你要定位的元素的属性是动态的,即每次打开页面,这个元素的id或者class等元素属性是动态生成的。代码执行时,元素的属性已经与之前不同,用之前的属性值去定位自然是定位不到的,会抛出NoSuchElementException。
怎么判断元素属性是否是动态?一般看到元素属性里有拼接一串数字的,就很有可能是动态的。想要分辨,刷新一下浏览器再看该元素,属性值中的数字串改变了,即是动态属性了。如下:<div id="btn-attention_2030295">...</div>
怎么定位这类型的元素呢?
1. 根据其他属性定位
如果有其他固定属性,最先考虑的当然是根据元素的其他属性来定位,定位方式那么多,何必在这一棵树上吊死。。
2. 根据相对关系定位
根据其附近的父节点、子节点、兄弟节点定位,关于这方面,博主之前的一篇文章可作为参考:Python selenium —— 父子、兄弟、相邻节点定位方式详解
3. 根据DOM顺序index定位
这个很简单,找到该元素在主文档或某级父节点中的index,然后根据index可轻松定位,不过这种方式可能不够稳定,如果可以,还是用其他的方法定位更加合适。
4. 根据部分元素属性定位
xpath中提供了三个非常好的方法来为我们定位部分属性值:
driver.find_element_by_xpath("//div[contains(@id, 'btn-attention')]")
driver.find_element_by_xpath("//div[starts-with(@id, 'btn-attention')]")
driver.find_element_by_xpath("//div[ends-with(@id, 'btn-attention')]") # 这个需要结尾是‘btn-attention’
contains(a, b) 如果a中含有字符串b,则返回true,否则返回false
starts-with(a, b) 如果a是以字符串b开头,返回true,否则返回false
ends-with(a, b) 如果a是以字符串b结尾,返回true,否则返回false
各种浏览器对xpath的支持情况不一样,像IE就差点,所以有时候会出现xpath在一个浏览器能定位到但在另一个浏览器定位不到的问题。
selenium父子、兄弟、相邻节点定位方式详解
实际应用中会遇到想定位的节点无法直接定位,需要通过附近节点来相对定位的问题,但从父节点定位子节点容易,从子节点定位父节点、定位一个节点的哥哥节点就一筹莫展了。
1. 由父节点定位子节点
最简单的肯定就是由父节点定位子节点了,我们有很多方法可以定位,下面上个例子:
<html>
<body>
<div id="A">
<!--父节点定位子节点-->
<div id="B">
<div>parent to child</div>
</div>
</div>
</body>
</html>
想要根据 B节点定位无id的子节点,代码示例如下:
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('D:\\py\\AutoTestFramework\\src\\others\\test.html')
# 1.串联寻找
print driver.find_element_by_id('B').find_element_by_tag_name('div').text
# 2.xpath父子关系寻找
print driver.find_element_by_xpath("//div[@id='B']/div").text
# 3.css selector父子关系寻找
print driver.find_element_by_css_selector('div#B>div').text
# 4.css selector nth-child
print driver.find_element_by_css_selector('div#B div:nth-child(1)').text
# 5.css selector nth-of-type
print driver.find_element_by_css_selector('div#B div:nth-of-type(1)').text
# 6.xpath轴 child
print driver.find_element_by_xpath("//div[@id='B']/child::div").text
driver.quit()
结果:
parent to child
parent to child
parent to child
parent to child
parent to child
parent to child
第4种方法用到了css选择器:nth-child(n),该选择器返回第n个节点,该节点为div标签;第5种方法用到了另一个css选择器: nth-of-type(n),该选择器返回第n个div标签,注意与上一个选择器的区别;第6种方法用到了xpath轴 child,这个是xpath默认的轴,可以忽略不写,其实质是跟方法2一样的。当然,css中还有一些选择器是可以选择父子关系的如last-child、nth-last-child等,感兴趣可以自行百度,有机会博主会讲讲css selector。
2. 由子节点定位父节点
由子节点想要定位到父节点就有点难度了,对以下代码:
<html>
<body>
<div id="A">
<!--子节点定位父节点-->
<div>
<div>child to parent
<div>
<div id="C"></div>
</div>
</div>
</div>
</div>
</body>
</html>
我们想要由 C节点 定位其两层父节点的div,示例代码如下:
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('D:\\py\\AutoTestFramework\\src\\others\\test.html')
# 1.xpath: `.`代表当前节点; '..'代表父节点
print driver.find_element_by_xpath("//div[@id='C']/../..").text
# 2.xpath轴 parent
print driver.find_element_by_xpath("//div[@id='C']/parent::*/parent::div").text
driver.quit()
结果:
child to parent
child to parent
这里我们有两种办法,第1种是 .. 的形式,就像我们知道的,. 表示当前节点,.. 表示父节点;第2种办法跟上面一样,是xpath轴中的一个:parent,取当前节点的父节点。这里也是css selector的一个痛点,因为css的设计不允许有能够获取父节点的办法(至少目前没有)
3. 由弟弟节点定位哥哥节点
这是第3、第4种情况,我们这里要定位的是兄弟节点了。如以下源码:
<html>
<body>
<div>
<!--下面两个节点用于兄弟节点定位-->
<div>brother 1</div>
<div id="D"></div>
<div>brother 2</div>
</div>
</body>
</html>
怎么通过 D节点 定位其哥哥节点呢?看代码示例:
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('D:\\Code\\py\\AutoTestFramework\\src\\others\\test.html')
# 1.xpath,通过父节点获取其哥哥节点
print driver.find_element_by_xpath("//div[@id='D']/../div[1]").text
# 2.xpath轴 preceding-sibling
print driver.find_element_by_xpath("//div[@id='D']/preceding-sibling::div[1]").text
driver.quit()
结果
brother 1
brother 1
这里博主也列举了两种方法,一种是通过该节点的父节点来获得哥哥节点,另外一种比较优雅,是通过 xpath轴:preceding-sibling,其能够获取当前节点的所有同级哥哥节点,注意括号里的标号,1 代表着离当前节点最近的一个哥哥节点,数字越大表示离当前节点越远,当然,xpath轴:preceding也可以,但是使用起来比较复杂,它获取到的是该节点之前的所有非祖先节点(这里不太好解释,改天专门写篇博文讲解下所有的轴)
4. 由哥哥节点定位弟弟节点
源码与 3 一致,要想通过 D节点 定位其弟弟节点,看代码示例:
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('D:\\Code\\py\\AutoTestFramework\\src\\others\\test.html')
# 1.xpath,通过父节点获取其弟弟节点
print driver.find_element_by_xpath("//div[@id='D']/../div[3]").text
# 2.xpath轴 following-sibling
print driver.find_element_by_xpath("//div[@id='D']/following-sibling::div[1]").text
# 3.xpath轴 following
print driver.find_element_by_xpath("//div[@id='D']/following::*").text
# 4.css selector +
print driver.find_element_by_css_selector('div#D + div').text
# 5.css selector ~
print driver.find_element_by_css_selector('div#D ~ div').text
driver.quit()
结果:
brother 2
brother 2
brother 2
brother 2
brother 2
第二种用到了xpath轴:following-sibling,跟preceding-sibling类似,它的作用是获取当前节点的所有同级弟弟节点,同样,1 代表离当前节点最近的一个弟弟节点,数字越大表示离当前节点越远;第三种用到了xpath轴:following,获取到该节点之后所有节点,除了祖先节点(跟preceding方向相反,但因为往下顺序容易读,不容易出错,所以也是可以用来获取弟弟节点的,但也不建议这么使用);第四、第五种,我们用到了css selector,+ 和 ~ 的区别是: + 表示紧跟在当前节点之后的div节点,~ 表示当前节点之后的div节点,如果用find_elements,则可获取到一组div节点。
selenium之 富文本框和editor编辑器的处理
一般输入框有三种:
1、短的input框,<input id="zenInput2" class="zenInputDemo" type="text" style="position: static;">
2、textarea框, <textarea id="message1" name="message1"></textarea>
3、div式的editor框,如下:
也可能是更复杂的iframe的editor,如下:
下面依次看看这几种输入框该怎么解决:
1. input
其实这个只是列在这里,input该如何处理,我想懂点selenium的都知道怎么办。
2.textarea
很简单,定位到元素,直接send_keys就行。示例网址:http://www.sucaijiayuan.com/api/demo.php?url=/demo/20150325-1
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get('http://www.sucaijiayuan.com/api/demo.php?url=/demo/20150325-1')
driver.maximize_window()
driver.switch_to.frame('iframe')
driver.find_element_by_id('message1').send_keys('Hello world!') # 很简单,直接send_keys就行
sleep(2)
print driver.find_element_by_id('message1').get_attribute('value')
driver.quit()
结果:
Hello world!
3.div式的editor
这种一样,定位到元素div,直接send_keys就行,不过这个send_keys不是到了‘value’属性中,而是在text中。示例网址:http://www.sucaijiayuan.com/api/demo.php?url=/demo/基于bootstrap的轻量级jQuery文本编辑器插件%20LineControl/index.html
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get('http://www.sucaijiayuan.com/api/demo.php?url=/demo/%E5%9F%BA%E4%BA%8Ebootstrap%E7%9A%84%E8%BD%BB%E9%87%8F%E7%BA%A7jQuery%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A8%E6%8F%92%E4%BB%B6%20LineControl/index.html')
driver.maximize_window()
driver.switch_to.frame('iframe')
driver.find_element_by_class_name('Editor-editor').send_keys('Hello world again!') # 没什么区别,也是直接send_keys
sleep(2)
print driver.find_element_by_class_name('Editor-editor').text
driver.quit()
结果:
Hello world again!
4.iframe中的editor
这种是最复杂的一种,但要搞明白了,其实也很简单。示例网址:http://ueditor.baidu.com/website/examples/completeDemo.html
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Chrome(executable_path='D:\py\AutoTestFramework\drivers\chromedriver.exe')
driver.get('http://ueditor.baidu.com/website/examples/completeDemo.html')
driver.switch_to.frame('ueditor_0') # 注意,这种editor一定有frame,一定要切frame
body_string = """Hello world again again!
Hello world again again!
Hello world again again!
Hello world again again!"""
driver.find_element_by_tag_name('body').send_keys(body_string) # 直接往frame里的body里填内容,是不是很简单粗暴
print driver.find_element_by_tag_name('body').text
driver.quit()
结果:
Hello world again again!
其实frame editor的内容一般都是写在里面的body里,最重要的就是切到frame中去。frame中一般是一个空的html,其中显示的内容即是body中的内容。
JavaScript及DOM介绍
一开始,DOM是为方便Javascript操作设计的API。但其实发展到后来,他们是两个独立的个体。而且Javascript不是唯一可以使用DOM的编程语言,比如python也可以访问DOM。DOM是一个API,HTML是一种标记语言,HTML在DOM的模型标准中被视为对象。DOM定义了HTML文档和XML文档的逻辑结构,给出了一种访问和处理这两种文档的方法。文档代表的是数据,而DOM则代表了如何去处理这些数据。DOM由三部分组成,包括:核心(core)、HTML接口和XML接口。我们可以使用JS语言来操作DOM从而来改变网页,但是要改变页面的某个东西,JS就需要获得对HTML文档中所有元素进行访问的入口。这个入口连同对HTML元素进行添加、移动、改变后移除的方法和属性,都是通过DOM来获得的。单理解就是对网页内容进行修改的接口。
DOM只提供编程接口,却无法实际操作HTML里面的内容。每个Web浏览器都会使用DOM,所以页面可以被脚本语言访问。而所有的浏览器都是以Javascript作为默认的脚本语言,所以前端工程师可以直接通过DOM模型在浏览器端上操作HTML,通过<script>标签载入或者直接插入Javascript脚本,通过DOM直接操作浏览器端的HTML文件。正是这种无缝融合,导致DOM和HTML的概念很容易混淆。
完整的JavaScript实现包含三个部分:ECMAScript,DOM,BOM(浏览器对象模型)
DOM对象:
- DOM对象主要指代网页内的标签【包括整个网页】
比如:document代表整个 HTML 文档,用来访问页面中的所有标签。
比如:form代表 <form> 元素
这里主要介绍使用document来查找标签和操作标签:
查找标签:
- 根据id查找标签:getElementById()
- 根据Name属性查找标签(多个):getElementsByName()
- 根据标签名查找标签(多个):getElementsByTagName()
- 根据class属性查找标签(多个):getElementsByClassName()
操作标签:
结点操作:
- 创建新的标签节点document.createElement('标签名');如document.createElement(‘p')
- appendChild(x):给标签增加子标签,x可以是html格式的,也可以是一个结点变量。如:
var para=document.creatElement(”p”);
document.getElementById(“div1”).appendChild(para);
- 删除结点:removeChild(x)
var parent=document.getElementById (” div1”);
var child=document.getElementById (” p1”);
parent.removeChild(child);
- 获取第一个孩子结点:firstChild
Document.getElementsByTagName(“html‘)[0].firstchild
<head>…</head>【注意:注释、回车等都算结点。】
- 获取最后一个孩子结点:lastChild
- 获取所有孩子结点:childNodes
- 获取前一个兄弟结点:previousSiblings
- 获取下一个兄弟结点:nextSiblings
Document.getElementsById(“i1‘). nextSibling
- 获取父结点:parentNode
属性和样式操作:
- 修改样式:style.样式=值Document.getElementsById(“p2‘).style.colo=”blue”【对于名字长的,名字后面那个单词要大写,比如backgroundColor】
- 修改属性:setAttribute(属性名,属性值)
- 获取属性:getAttribute(属性名) Document.getElementsById(“i1‘).getAttibute(”value“)
- 删除属性:removeAttribute(属性名) Document.getElementsById(“i2‘).removeAttibute(”value“)
文本操作:
- 获取或修改标签内的html内容:innerHTML
Document.getElementsById(“p1‘).innerHTML=”NEW text!”
- 修改内部的文本内容:
Document.getElementsById(“i4‘). innerText=”hello”;
Window 对象:
Window 对象表示浏览器窗口
Window 对象一般与浏览器窗口的操作相关,比如提示框,提示框也是非网页内容。
但凡是涉及浏览器的,都通过window对象来操作。
location位置对象:
- 常用属性:
获取当前网页的地址:href
- 常用方法:
刷新网页:reload
加载新网页:assign(url)
定时器设置:
- 设置一次性定时器setTimeout(执行代码,毫秒数)
- 设置重复运行的定时器setInterval(执行代码,间隔毫秒数)
- 清除一次性定时器:clearTimeout(定时器名)
- 清除重复运行的定时器:clearInterval(定时器名)
对话框设置:
- 弹出一个提示框:alert(提示内容)
- 弹出一个询问框(有确认和取消按钮):confirm(提示内容)
- 弹出一个输入框:prompt(提示内容),会把输入的内容返回
<input id='i1' type="button" value="点击" οnclick="alert('提示内容')">
<input id='i2' type="button" value="点击2" οnclick="confirm('提示内容')">
<input id='i3' type="button" value="点击3" οnclick="prompt('提示内容')">
窗口控制:
- 页面滚动指定长度:ScrollBy(水平偏移量[,垂直偏移量])
XPath 语法
以下面这个 XML 文档为例。
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
表达式 | 描述 |
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性
|
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
路径表达式 | 结果 |
/bookstore/book[1] | 选取属于bookstore子元素的第一个book元素。 |
/bookstore/book[last()] | 选取属于bookstore子元素的最后一个book元素。 |
/bookstore/book[last()-1] | 选取属于bookstore子元素的倒数第二个book元素。 |
/bookstore/book[position()<3] | 选取最前面的两个属于bookstore元素的子元素的book元素。 |
//title[@lang] | 选取所有拥有名为lang的属性的title元素。 |
//title[@lang='eng'] | 选取所有title元素,且这些元素拥有值为eng的lang属性。 |
/bookstore/book[price>35.00] | 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00。 |
/bookstore/book[price>35.00]/title | 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00。 |
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
/bookstore/* 选取bookstore元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的title元素。
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。