整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

快速了解JavaScript文本框操作

TML 中使用 <input> 元素表示单行输入框和 <textarea> 元素表示多行文本框。

HTML中使用的 <input> 元素在 JavaScript 中对应的是 HTMLInputElement 类型。HTMLInputElement 继承自 HTMLElement 接口:

interface HTMLInputElement extends HTMLElement {
    ...
}

HTMLInputElement 类型有一些独有的属性和方法:

  • name:表示 <input> 元素的名称。
  • disabled:表示 <input> 元素是否禁止使用。在表单提交时不会包含被禁止的 <input> 元素。
  • autofocus:表示页面加载时,该元素是否会自动获得焦点。
  • required:表示表单提交时,该 <input>元素是否为必填。
  • value:表示该 <input> 元素的值。
  • validity:返回一个 ValidityState 对象,表示 <input> 元素的校验状态。是只读属性。
  • validationMessage:表示 <input> 元素校验失败时,用户看到的报错信息。如果该元素不需要校验,或者通过校验,该属性为空字符串。是只读属性。
  • willValidate:表示表单提交时,<input> 元素是否会被校验。是只读属性。
  • select():选中 <input> 元素内部的所有文本。
  • click():模拟鼠标点击当前的 <input> 元素。

而在上述介绍 HTMLInputElement 类型中的属性时,type 属性要特别关注一下,因为根据 type 属性的改变,可以改变<input>的属性。

类型

描述

text

文本输入

password

密码输入

submit

表单数据提交

button

按钮

radio

单选框

checkbox

复选框

file

文件

hidden

隐藏的字段

image

定义图像作为提交按钮

reset

重置按钮

省略 type 属性与 type="text"效果一样, <input> 元素显示为文本框。

type 的值为text/password/number/时,会有以下属性对 <input> 元素有效。

属性

类型

描述

autocomplete

string

字符串on或off,表示<input>元素的输入内容可以被浏览器自动补全。

maxLength

long

指定<input>元素允许的最多字符数。

size

unsigned long

表示<input>元素的宽度,这个宽度是以字符数来计量的。

pattern

string

表示<input>元素的值应该满足的正则表达式

placeholder

string

表示<input>元素的占位符,作为对元素的提示。

readOnly

boolean

表示用户是否可以修改<input>的值。

min

string

表示<input>元素的最小数值或日期。

max

string

表示<input>元素的最大数值或日期。

selectionStart

unsigned long

表示选中文本的起始位置。如果没有选中文本,返回光标在<input>元素内部的位置。

selectionEnd

unsigned long

表示选中文本的结束位置。如果没有选中文本,返回光标在<input>元素内部的位置。

selectionDirection

string

表示选中文本的方向。可能的值包括forwardbackwardnone

下面创建一个 type="text" ,一次显示 25 个字符,但最多允许显示 50 个字符的文本框:

<input type="text" size="25" maxlength="50" value="initial value">

HTML 使用的 <textarea> 元素在 JavaScript 中对应的是 HTMLTextAreaElement 类型。HTMLTextAreaElement类型继承自 HTMLElement 接口:

interface HTMLTextAreaElement extends HTMLElement {
    ...
}

HTMLTextAreaElement 类型有一些独有的属性和方法:

  • form:只读属性,返回对父表单元素的引用。如果此元素未包含在表单元素中,则它可以是同一文档中 id 属性同 form 值的 <form> 元素,如果没有,该值为 null
  • type:只读属性,返回字符串textarea
  • valuestring类型,返回/设置控件中包含的原始值。
  • textLength:只读属性,long类型,返回<textarea>元素的value值的长度。与 value.length 相同。
  • defaultValuestring类型,返回/设置textarea元素的默认值。
  • placeholderstring类型,返回/设置 textarea 元素的占位符,作为对输入内容的提示。
  • rowsunsigned long类型,返回/设置textarea元素的可见文本行数。
  • colsunsigned long类型,返回/设置 textarea 元素的可见宽度。
  • autofocusboolean类型,返回/设置textarea元素在页面加载时自动获取焦点。
  • name:返回/设置 textarea 元素的名称。
  • disabledboolean类型,返回/设置 textarea 元素是否应当被禁用。
  • HTMLTextAreaElement.labelsNodeList类型,返回与此select元素关联的标签元素列表。
  • maxLengthlong类型,返回/设置元素可以输入的最大字符数。仅在值更改时才评估此约束。
  • minLengthlong类型,返回/设置元素可以输入的最小字符数。仅在值更改时才评估此约束。
  • accessKeyboolean类型,返回/设置访问 textarea 的键盘快捷键。
  • readOnlyboolean类型,返回/设置 textarea 元素是否为只读。
  • requiredboolean 类型,返回/设置 textarea 元素是否为必填项。
  • tabIndexlong类型,返回/设置 textarea 元素的 tab 键导航顺序中元素的位置。

下面创建一个高度为 25,宽度为 5 的 <textarea> 多行文本框。它与 <input> 不同的是,初始值显示在 <textarea>...</textarea> 之间:

<textarea rows="25" cols="5">initial value</textarea>

注意:处理文本框值的时候最好不要使用 DOM 方法,而应该使用 value 属性。

选择文本

<input><textarea> 都支持 select() 方法,该方法用于选中文本框中的所有内容。该方法的语法为:

select(): void

下面看一个示例:

let textbox = document.forms[0].elements["input-box"];
textbox.select();

也可以在文本框获得焦点时,选中文本框的内容:

textbox.addEventListener("focus", (event) => {
    event.target.select();
});

select 事件

当选中文本框中的文本或使用 select() 方法时,会触发 select 事件。

let textbox = document.forms[0].elements["textbox1"];
textbox.addEventListener("select", (event) => {
    console.log(`Text selected: ${textbox.value}`);
});

取得选中的文本

HTML5 对 select 事件进行了扩展,通过 selectionStartselectionEnd 属性获取文本选区的起点偏移量和终点偏移量。如下所示:

function getSelectedText(textbox){
    return textbox.value.substring(textbox.selectionStart,
textbox.selectionEnd);
}

注意:在 IE8 及更早版本不支持这两个属性。

选中部分文本

HTML5 提供了 setSelectionRange() 方法用于选中部分文本:

setSelectionRange(start, end, direction): void;
  • start:被选中的第一个字符的位置索引,从 0 开始。如果这个值比元素的 value 长度还大,则会被看做 value 最后一个位置的索引。
  • end:被选中的最后一个字符的下一个位置索引。如果这个值比元素的 value 长度还大,则会被看做 value 最后一个位置的索引。
  • direction:该参数是可选的字符串类型,表示选择方向,可以传入 “forward”/“backward”/“none” 三个值。

下面看一个例子:

<input type="text" id="text-sample" size="20" value="Hello World!">
<button onclick="selectText()">选中部分文本</button>
<script>
    function selectText() {
        let input = document.getElementById("text-sample");
        input.focus();
        input.setSelectionRange(4, 8);    // o Wo
    }
</script>

如果想要看到选中效果,必须让文本框获得焦点。

输入过滤

不同文本框经常需要保证输入特定类型或格式的数据,或许数据需要包含特定字符或必须匹配某个特定模式。而文本框并未提供验证功能,因此要配合 JavaScript 脚本实现输入过滤功能。

屏蔽字符

有些输入框需要出现或不出现特定字符。如果想要将输入框变成只读的,只需要使用 preventDefault()方法将按键都屏蔽:

input.addEventListener("keypress", (event) => {
    event.preventDefault();
});

而要屏蔽特定字符,就需要检查事件的 charCode 属性。如下所示,使用正则表达式实现只允许输入数字的输入框:

input.addEventListener("keypress", (event) => {
    if (!/\d/.test(event.key)) {
        event.preventDefault();
    }
});

还有一个问题需要处理:复制、粘贴及涉及Ctrl 键的其他功能。在除IE 外的所有浏览器中,前面代码会屏蔽快捷键Ctrl+C、Ctrl+V 及其他使用Ctrl 的组合键。因此,最后一项检测是确保没有按下Ctrl键,如下面的例子所示:

textbox.addEventListener("keypress", (event) => {
if (!/\d/.test(String.fromCharCode(event.charCode)) &&
event.charCode > 9 &&
!event.ctrlKey){
event.preventDefault();
}
});

最后这个改动可以确保所有默认的文本框行为不受影响。这个技术可以用来自定义是否允许在文本框中输入某些字符。

处理剪贴板

IE 是第一个实现了剪切板相关的事件以及通过JavaScript访问剪切板数据的浏览器,其它浏览器在后来也都支持了相同的事件和剪切板的访问,后来 HTML5 将其纳入了规范。以下是与剪切板相关的 6 个事件:

  • beforecopy:发生复制操作前触发。
  • copy:发生复制操作时触发。
  • beforecut:发生剪切操作前触发。
  • cut:发生剪切操作时触发。
  • beforepaste:发生粘贴操作前触发。
  • paste:发生粘贴操作时触发。

剪切板事件的行为及相关对象会因浏览器而异。在 Safari、Chrome 和 Firefox 中,beforecopybeforecutbeforepaste 事件只会在显示文本框的上下文菜单时触发,但 IE 不仅在这种情况下触发,也会在 copycutpaste 事件在所有浏览器中都会按预期触发。

在实际的事件发生之前,通过beforecopybeforecutbeforepaste 事件可以在向剪贴板发送或从中检索数据前修改数据。不过,取消这些事件并不会取消剪贴板操作。要阻止实际的剪贴板操作,必须取消 copycutpaste 事件。

剪贴板的数据通过 clipboardData 对象来获取,且clipboardData 对象提供 3 个操作数据的方法:

  • getData(format):从剪贴板获取指定格式的数据,接收的参数为指定获取的数据格式,如果没有指定数据格式或指定数据格式不存在,则此方法返回一个空字符串。
  • setData(format, data):给剪贴板赋予指定格式的数据,第一个参数指定数据格式,第二个参数为第一个参数赋予数据。如果指定数据格式不存在,则将其添加到数据存储的末尾;如果指定数据格式存在,则将数据替换在指定数据格式的位置。
  • clearData(format):删除指定格式的数据,接收的参数为指定要删除的数据格式,如果指定格式不存在,则此方法不执行任何操作;如果此参数为空字符串或未提供,则将删除所有格式的数据。

clipboardData 对象在 IE 中使用 window 获取,在 Firefox、Safari 和 Chrome 中使用 event 获取。为防止未经授权访问剪贴板,只能在剪贴板事件期间访问 clipboardData 对象;IE 会在任何时候都暴露 clipboardData 对象。因此,要兼容两者,最好在剪贴板事件期间使用该对象。

function getClipboardText(event){
    var clipboardData = (event.clipboardData || window.clipboardData);
    return clipboardData.getData("text");
}
function setClipboardText (event, value){
    if (event.clipboardData){
        return event.clipboardData.setData("text/plain", value);
    } else if (window.clipboardData){
        return window.clipboardData.setData("text", value);
    }
}

如果文本框只有数字,那剪贴时,就需要使用paste事件检查剪贴板上的文本是否无效。如果无效,可以取消默认行为:

input.addEventListener("paste", (event) => {
    let text = getClipboardText(event);
    if (!/^\d*$/.test(text)){
        event.preventDefault();
    }
});

注意:Firefox、Safari和Chrome只允许在onpaste事件中访问getData()方法。

自动切换

在 JavaScript 中,可以用在当前字段完成时自动切换到下一个字段的方式来增强表单字段的易用性。比如,常用手机号分为国家好加手机号。因此,我们设置 2 个文本框:

<form>
    <input type="text" name="phone1" id="phone-id-1" maxlength="4">
    <input type="text" name="phone2" id="phone-id-2" maxlength="11">
</form>

当文本框输入到最大允许字符数后,就把焦点移到下一个文本框,这样可以增加表单的易用性并加速数据输入。如下所示:

<script>
    function tabForward(event){
        let target = event.target;
        if (target.value.length == target.maxLength){
            let form = target.form;
            for (let i = 0, len = form.elements.length; i < len; i++) {
                if (form.elements[i] == target) {
                    if (form.elements[i+1]) {
                        form.elements[i+1].focus();
                    }
                    return;
                }
            }
        }
    }
    let inputIds = ["phone-id-1", "phone-id-2"];
    for (let id of inputIds) {
        let textbox = document.getElementById(id);
        textbox.addEventListener("keyup", tabForward);
    }
</script>

这里,tabForward() 函数通过比较用户输入文本的长度与 maxLength 属性的值来检测输入是否达到了最大长度。如果两者相等,就通过循环表中的元素集合找到当前文本框,并把焦点设置到下一个元素。

注意:上面的代码只适用于之前既定的标记,没有考虑可能存在的隐藏字段。

HTML5

HTML5 新增了一些表单提交前,浏览器会基于指定的规则进行验证,并在出错时显示适当的错误信息。而验证会基于某些条件应用到表单字段中。

必填字段

表单字段中添加 required 属性,用于标注该字段是必填项,不填则无法提交。该属性适用于<input><textarea><select>。如下所示:

<input type="text" name="account" required>

也可以通过 JavaScript 检测对应元素的 required 属性来判断表单字段是否为必填项:

let isRequired = document.forms[0].elements["account"].required;

也可以检测浏览器是否支持 required 属性:

let isRequiredSupported = "required" in document.createElement("input");

注意:不同浏览器处理必填字段的机制不同。Firefox、Chrome、IE 和Opera 会阻止表单提交并在相应字段下面显示有帮助信息的弹框,而Safari 什么也不做,也不会阻止提交表单。

更多输入类型

HTML5 为 <input> 元素增加了几个新的 type 值。如下所示:

类型

描述

number

数字值的输入

date

日期输入

color

颜色输入

range

一定范围内的值的输入

month

允许用户选择月份和年份

week

允许用户选择周和年份

time

允许用户选择时间(无时区)

datetime

允许用户选择日期和时间(有时区)

datetime-local

允许用户选择日期和时间(无时区)

email

电子邮件地址的输入

search

搜索(表现类似常规文本)

tel

电话号码的输入

url

URL地址的输入

这些输入表名字段应该输入的数据类型,并且提供了默认验证。如下所示:

<input type="email" name="email">
<input type="url" name="homepage">

要检测浏览器是否支持新类型,可以在 JavaScript 中创建 <input> 并设置 type 属性,之后读取它即可。老版本中会将我只类型设置为 text,而支持的会返回正确的值。如下所示:

let input = document.createElement("input");
input.type = "email";
let isEmailSupported = (input.type == "email");

数值范围

而上面介绍的几个如 number/range/datetime/datetime-local/date/month/week/time 几个填写数字的类型,都可以指定 min/max/step 等几个与数值有关的属性。step 属性用于规定合法数字间隔,如 step="2",则合法数字应该为 0、2、4、6,依次类推。如下所示:

<input type="number" min="0" max="100" step="5" name="count">

上面的例子是<input>中只能输入从 0 到 100 中 5 的倍数。

也可以使用 stepUp()stepDown() 方法对 <input> 元素中的值进行加减,它俩会接收一个可选参数,用于表示加减的数值。如下所示:

input.stepUp(); // 加1
input.stepUp(5); // 加5
input.stepDown(); // 减1
input.stepDown(10); // 减10

输入模式

HTML5 还为文本添加了 pattern 属性,用于指定一个正则表达式。这样就可以自己设置 <input> 元素的输入模式了。如下所示:

<input type="text" pattern="\d+" name="count">

注意模式的开头和末尾分别假设有^$。这意味着输入内容必须从头到尾都严格与模式匹配。

与新增的输入类型一样,指定 pattern 属性也不会阻止用户输入无效内容。模式会应用到值,然后浏览器会知道值是否有效。通过访问 pattern 属性可以读取模式:

let pattern = document.forms[0].elements["count"].pattern;

使用如下代码可以检测浏览器是否支持pattern 属性:

let isPatternSupported = "pattern" in document.createElement("input");

检测有效性

HTML5 新增了 checkValidity() 方法,用来检测表单中任意给定字段是否有效。而判断的条件是约束条件,因此必填字段如果没有值会被视为无效,字段值不匹配 pattern 属性也会被视为无效。如下所示:

if (document.forms[0].elements[0].checkValidity()){
    // 字段有效,继续
} else {
    // 字段无效
}

要检查整个表单是否有效,可以直接在表单上调用checkValidity()方法。这个方法会在所有字段都有效时返回true,有一个字段无效就会返回false

if(document.forms[0].checkValidity()){
    // 表单有效,继续
} else {
    // 表单无效
}

validity 属性会返回一个ValidityState 对象,表示 <input> 元素的校验状态。返回的对象包含一些列的布尔值的属性:

  • customError:如果设置了 setCustomValidity() 就返回 true,否则返回false。
  • patternMismatch:如果字段值不匹配指定的 pattern 属性则返回true。
  • rangeOverflow:如果字段值大于 max 的值则返回true。
  • rangeUnderflow:如果字段值小于 min 的值则返回true。
  • stepMisMatch:如果字段值与 minmaxstep 的值不相符则返回true。
  • tooLong:如果字段值的长度超过了 maxlength 属性指定的值则返回true。
  • typeMismatch:如果字段值不是 "email""url" 要求的格式则返回true。
  • valid:如果其他所有属性的值都为false 则返回true。与checkValidity()的条件一致。
  • valueMissing:如果字段是必填的但没有值则返回true。

因此,通过 validity 属性可以检查表单字段的有效性,从而获取更具体的信息,如下所示:

if (input.validity && !input.validity.valid){
    if (input.validity.valueMissing){
        console.log("请指定值.")
    } else if (input.validity.typeMismatch){
        console.log("请指定电子邮件地址.");
    } else {
        console.log("值无效.");
    }
}

禁用验证

通过指定 novalidate 属性可以禁止对表单进行任何验证:

<form method="post" action="/signup" novalidate>
    <!-- 表单元素 -->
</form>

也可以在 JavaScript 通过 noValidate 属性设置,为 true 表示属性存在,为 false 表示属性不存在:

document.forms[0].noValidate = true; // 关闭验证

如果一个表单中有多个提交按钮,那么可以给特定的提交按钮添加formnovalidate 属性,指定通过该按钮无需验证即可提交表单:

<form method="post" action="/foo">
    <!-- 表单元素 -->
    <input type="submit" value="注册提交">
    <input type="submit" formnovalidate name="btnNoValidate"
value="没有验证的提交按钮">
</form>

也可以使用 JavaScript 设置 formNoValidate 属性:

// 关闭验证
document.forms[0].elements["btnNoValidate"].formNoValidate = true;

总结

以上总结了 <input><textarea> 两个元素的一些功能,主要是 <input> 元素可以通过设置 type 属性获取不同类型的输入框,可以通过监听键盘事件并检测要插入的字符来控制文本框的内容。

还有一些与剪贴板相关的事件,并对剪贴的内容进行检测。还介绍了一些 HTML5 新增的属性和方法和新增的更多的 <input> 元素的类型,和一些与验证相关的属性和方法。

一种:selenium导入浏览器驱动,用get方法打开浏览器,例如:

import time
from selenium import webdriver

def mac():
 # browser = webdriver.Chrome()
 # browser = webdriver.Firefox()
 browser = webdriver.Ie()
 browser.implicitly_wait(5)
 browser.get('http://www.baidu.com/')

第二种:通过导入python的标准库webbrowser打开浏览器,例如:

import webbrowser

def mac():
 # web.open(‘http://www.baidu.com’,new=0,autoraise=True) # new:0/1/2 0:同一浏览器窗口打开 1:打开浏览器新的窗口,2:打开浏览器窗口新的tab; autoraise=True:窗口自动增长
 # web.open_new_tab(‘http://www.baidu.com’)
 web.open_new(‘http://www.baidu.com’)

第三种:使用Splinter模块模块

一、Splinter 的安装
Splinter 的使用必修依靠 Cython、lxml、selenium 这三个软件。所以,安装前请提前安装 Cython、lxml、selenium。

二、Splinter 的使用
这里,我给出自动登录 126 邮箱的案例。难点是要找到页面的账户、密码、登录的页面元素,这里需要查看 126 邮箱登录页面的源码,才能找到相关控件的 id.

例如: 输入密码,密码的文本控件 id 是 pwdInput. 可以使用browser.find_by_id() 方法定位到密码的文本框,接着使用fill() 方法,填写密码。至于模拟点击按钮,也是要先找到按钮控件的 id, 然后使用 click() 方法。

# coding=utf-8
import time 
from splinter import Browser

def splinter(url):
 browser = Browser()
 # login 126 email websize
 browser.visit(url)
 # wait web element loading
 time.sleep(5)
 # fill in account and password
 browser.find_by_id('idInput').fill('xxxxxx')
 browser.find_by_id('pwdInput').fill('xxxxx')
 # click the button of login
 browser.find_by_id('loginBtn').click()
 time.sleep(8)
 # close the window of brower
 browser.quit()
	
if __name__ == '__main__': 
 websize3 ='http://www.126.com' 
 splinter(websize3) 

WebDriver简介

selenium 从 2.0 开始集成了 webdriver 的 API,提供了更简单,更简洁的编程接口。selenium webdriver 的目标是提供一个设计良好的面向对象的 API,提供了更好的支持进行 web-app 测试。

打开浏览器

在 selenium+python 自动化测试(一)–环境搭建中,运行了一个测试脚本,脚本内容如下:

from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
print(browser.title)
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
time.sleep(3)
browser.close() 

webdriver 是一个 Web 应用程序测试自动化工具,用来验证程序是否如预期的那样执行。

webdriver.Chrome():创建一个 Chrome 浏览器的 webdriver 实例

browser.get(“http://www.baidu.com“):打开”http://www.baidu.com”页面

browser.find_element_by_id(“kw”).send_keys(“selenium”):
找到 id 为“kw”的元素,在这个页面上为百度首页的搜索框,在其中输入“selenium”

browser.find_element_by_id(“su”).click():找到 id 为“su”的元素并点击,在这个页面上为百度首页的“百度一下”按钮

browser.close():退出浏览器

运行脚本的第一步是打开浏览器,使用 webdriver.Chrome() 打开谷歌浏览器,如果要指定其他浏览器,比如要使用 Firefox 或者 IE 浏览器,更换浏览器名称就可以了

browser = webdriver.Chrome() // 打开 Chrome 浏览器

browser = webdriver.Firefox() // 打开 Firefox 浏览器

browser = webdriver.Ie() // 打开 IE 浏览器

第二步操作是打开页面,使用browser.get(url)方法来打开网页链接,例如脚本中打开百度首页

browser.get("http://www.baidu.com")

接下来是 **print(browser.title)**,使用browser.title获取当前页面的title,title就是在浏览器 tab 上显示的内容,例如百度首页的标题是“百度一下,你就知道”

浏览器前进后退

在当前页面打开一个新的链接后,如果想回退到前一个页面,使用如下browser.back(),相当于点击了浏览器的后退按钮

和 back 操作对应的是浏览器前进操作browser.forward(),相当于点击了浏览器的前进按钮

browser.back() // 回到上一个页面

browser.forward() // 切换到下一个页面

浏览器运行后,如果页面没有最大化,可以调用browser.maximize_window()将浏览器最大化,相当于点击了页面右上角的最大化按钮

browser.maximize_window() // 浏览器窗口最大化

browser.set_window_size(800, 720) // 设置窗口大小为 800*720

浏览器截屏操作,参数是截屏的图片保存路径:

browser.get_screenshot_as_file("D:/data/test.png") 屏幕截图保存为***

browser.refresh() // 重新加载页面, 页面刷新

在测试脚本运行完后,一般会在最后关闭浏览器,有两种方法关闭浏览器,close()方法用于关闭当前页面,quit()方法关闭所有和当前测试有关的浏览器窗口

browser.close() // 关闭当前页面

browser.quit() // 关闭所有由当前测试脚本打开的页面

<h1 class="csdn_top" line-height:38px;color:#2c3033;padding:0px="" 29px;white-space:normal;"="" style="word-wrap: break-word; color: rgb(0, 0, 0); font-family: "sans serif", tahoma, verdana, helvetica; margin-top: 0px; margin-bottom: 0px; font-size: 24px;"> 页面元素定位

要定位页面元素,需要找到页面的源码。

IE 浏览器中,打开页面后,在页面上点击鼠标右键,会有“查看源代码”的选项,点击后就会进入页面源码页面,在这里就可以找到页面的所有元素

使用 Chrome 浏览器打开页面后,在浏览器的地址栏右侧有一个图标,点击这个图标后,会出现许多菜单项,选择更多工具里的开发者工具,就会出现页面的源码,不同版本的浏览器菜单选项可能不同,但是都会在开发者工具里找到页面的源码

Firefox 浏览器打开页面后,在右键菜单里也可以找到“查看页面源代码”的选项。在 Firefox 中,可以使用浏览器自带的插件查看定位元素,在 Firefox 的附加组件里搜索 firebug 进行下载,安装 firebug 组件后会在浏览器的工具栏中多出一个小虫子的图标,点击这个图标就可以打开组件查看页面源码,打开后如下图所示

以百度首页搜索页面为例,看一下 webdriver 定位元素的八种方式

使用id定位

在页面源码中找到搜索输入框的元素定义


可以看到输入框有一个有一个 id 的属性,调用find_element_by_id()根据 id 属性来找到元素,参数为属性的值

input_search = browser.find_element_by_id("kw")

使用name定位

使用find_element_by_name()根据 name 属性找到元素,参数为 name 属性的值

搜索框有一个 name=”wd”的属性,使用 name 查找搜索输入框元素

input_search = browser.find_element_by_name("wd")

使用className定位

使用find_element_by_class_name()根据 className 属性找到元素,参数为 className 属性的值

搜索框有一个 class=”s_ipt”的属性,使用 className 查找元素

input_search = browser.find_element_by_class_name("s_ipt")

使用tagName定位

使用find_element_by_tag_name()根据 tagName 属性找到元素,参数为元素标签的名称

每个页面的元素都有一个 tag,搜索框的标签为 input,有时候一个页面里有许多相同的标签,所以用这种方法找到的元素一般都不准确,除非这个元素使用的标签在这个页面里是唯一的。一般不会使用这种方式来定位元素

input_search = browser.find_element_by_class_name("input")

使用link_text定位

页面上都会有一些文本链接,点击链接后会打开一个新的页面,这些可以点击的链接可以使用find_element_by_link_text来定位,百度首页上方有如下几个元素

例如要定位“新闻”,找到元素的代码,有一个 href 的属性,这是点击后打开的页面

新闻

使用 link_text 查找元素,参数为元素的文本信息

news = browser.find_element_by_link_text("新闻")

使用partial_link_text定位

这种方式类似于 link_text 的定位方式,如果一个元素的文本过长,不需要使用文本的所有信息,可以使用其中的部分文本就可以定位

使用 partial_link_text 查找百度首页的“新闻”元素,参数为文本信息,可以使用全部的文本,也可以使用部分文本

news = browser.find_element_by_link_text("新闻") // 使用全部文本

news = browser.find_element_by_link_text("新") // 使用部分文本

使用css selector定位

使用 css 属性定位元素有多种方法,可以使用元素的 id、name、className,也可以使用元素的其他属性,如果一个元素没有上述的几种属性或者定位不到时,可以使用 css 来定位

还是使用百度搜索框的实例来说明 css 定位的用法

css使用元素的id定位

css 属性使用 id 定位时,使用 #号表示元素的 id

input_search = browser.find_element_by_css_selector("#kw") // 使用元素的 id 定位

css使用元素的class定位

css 属性使用 class 定位时,使用. 号表示元素的 class

input_search = browser.find_element_by_css_selector(".s_ipt") // 使用元素的 class 定位

css使用元素的tag定位

css 属性使用 tagName 定位时,直接使用元素的标签

input_search = browser.find_element_by_css_selector("input") // 使用元素的 tagName 定位

css 使用元素的其他属性

除了上述 3 种属性,css 属性可以使用元素的其他属性定位,格式如下

input_search = browser.find_element_by_css_selector("[maxlength='255']")

使用元素的maxlength属性定位

input_search = browser.find_element_by_css_selector("[autocomplete='off']")

使用元素的autocomplete属性定位

可以在参数中加入元素的标签名称

input_search = browser.find_element_by_css_selector("input#kw") // 使用元素的 id 定位

input_search = browser.find_element_by_css_selector("input.s_ipt") // 使用元素的 class 定位

input_search = browser.find_element_by_css_selector("input[maxlength='255']") // 使用元素的 maxlength 属性定位

input_search = browser.find_element_by_css_selector("input[autocomplete='off']") // 使用元素的 autocomplete 属性定位

css 的层级定位

当一个元素使用自身的属性不容易定位时,可以通过它的父元素来找到它,如果父元素也不好定位,可以再通过上元素来定位,以此类推,一直找到容易定位的父元素为止,通过层级定位到需要查找的元素

通过 Firefox 的 firebug 组件查看百度首页的源码

通过层级来定位搜索框

input_search = browser.find_element_by_css_selector("form#form>span:nth-child(1)>input")

input_search = browser.find_element_by_css_selector("form.fm>span:nth-child(1)>input")

搜索框的父元素为 span 标签,span 的父元素为 form,form 有 id 和 class 属性,可以通过这两个属性来定位,找到 form 元素后,form 下有多个 span 标签,所以要使用 span:nth-child(1),表示 form 下的第一个 span 标签,这种用法很容易理解,表示第几个孩子,最后是 span 下的 input 标签,span 下只有一个 input,所以就可以定位到搜索框

css逻辑运算

用一个属性来定位元素时,如果有其他元素的属性和此元素重复,可以组合多个属性来功共同定位

组合多个属性定位元素定位百度搜索框

input_search = browser.find_element_by_css_selector("input[id='kw'][name='wd']")

在元素内定义的属性,都可以使用 css 来定位,使用其他几种方式无法定位到元素时,可以使用 css,够强大!

使用xpath定位

XPath 是一种在 XML 文档中定位元素的语言。因为 HTML 可以看做 XML 的一种实现,所以 selenium 用户可是使用这种强大语言在 web 应用中定位元素。xpath 也可以通过元素的各种属性定位到元素

使用元素属性定位

input_search = browser.find_element_by_xpath("//*[@id='kw']") // 通过元素 id 查找元素

input_search = browser.find_element_by_xpath("//*[@name='wd']") // 通过元素 name 查找元素

input_search = browser.find_element_by_xpath("//*[@class='s_ipt']") // 通过元素 class 查找元素

input_search = browser.find_element_by_xpath("//*[@maxlength='255']") // 通过其他属性查找元素

input_search = browser.find_element_by_xpath("//*[@autocomplete='off']") // 通过其他属性查找元素

前面的 * 号表示查找所有的标签元素,可以替换为标签名称,更准确的定位元素

input_search = browser.find_element_by_xpath("//input[@id='kw']") // 通过元素 id 查找元素

input_search = browser.find_element_by_xpath("//input[@name='wd']") // 通过元素 name 查找元素

input_search = browser.find_element_by_xpath("//input[@class='s_ipt']") // 通过元素 class 查找元素

input_search = browser.find_element_by_xpath("//input[@maxlength='255']") // 通过其他属性查找元素

input_search = browser.find_element_by_xpath("//input[@autocomplete='off']") // 通过其他属性查找元素

xpath也可以通过层级来定位,定位方式

input_search = browser.find_element_by_xpath("//input[@id='form']//span[1]//input")

browser.find_element_by_xpath("//input[@class='fm']//span[1]//input")查找效果和通过 css 的层级定位是相同的,意思是 form 元素下面的第一个 span 元素的 input 标签子元素xpath的逻辑元素通过and运算符来组合元素属性

input_search = browser.find_element_by_xpath("//input[@id='kw' and name='wd']")

属性匹配
xpath 中还有一种更强大的定位方式,通过模糊匹配元素的属性

news = browser.find_element_by_xpath("//a[contains(text(), '新闻')]")

查找text中包含"新闻"的元素

input_search = browser.find_element_by_xpath("//input[contains(@id, 'kw']")

查找id中包含"kw"的元素

input_search = browser.find_element_by_xpath("//input[starts-with(@id, 'k']")

查找id以"k"开头的元素

input_search = browser.find_element_by_xpath("//input[ends-with(@id, 'w']")

查找id以"w"结尾的元素

input_search = browser.find_element_by_xpath("//input[matchs(@id, 'k*']")

利用正则表达式查找元素

上面介绍了查找页面元素的八种方法,通过这些方式找到的都是单个元素,如果需要批量查找元素,还有和上面方式对应的八种复数形式

find_elements_by_id

find_elements_by_name

find_elements_by_class_name

find_elements_by_tag_name

find_elements_by_link_text

find_elements_by_partial_link_text

find_elements_by_css_selector

find_elements_by_xpath

这8种方法查找到的是一组元素,返回的是list,可以通过索引来操作元素

例如页面上的复选框和单选框,或者页面上有多个属相相同的输入框,这些属性相同的一组元素,可以批量获取,然后过滤出需要操作的元素,选择其中的一个或者多个进行操作

通过百度首页搜索“selenium”关键字,会出现许多搜索结果,这些搜索结果具有相同的属性,不同的是属性的值不同,定位这些元素时,可以使用批量定位的方法

看下面的代码

这是搜索 selenium 关键字后的页面结果,每一个搜索结果都是可点击的链接,定位这些元素的方法:

search_results = browser.find_elements_by_css_selector("h.t>a")

search_results[3].click() // 通过索引点击第 4 条搜索结果

第二个例子

checkbox
checkbox1
checkbox2
checkbox3

这个页面上有 3 个复选框,打开后如下图所示:

操作复选框

// 查找所有的复选框并点击
checkboxs = browser.find_element_by_xpath('input[@type="checkbox"]')

返回一个 list

for checkbox in checkboxs:
checkbox.click()
// 点击最后一个复选框

checkboxs[2].click()

from selenium import webdriver

browser=webdriver.Firefox()

browser.get(r’http://www.baidu.com/’)

print(‘browser attributes:’)

print(dir(browser))

elem=browser.find_element_by_id(‘kw’)

print(‘WebElement attributes:’)

print(dir(elem))

浏览器属性:

driver attributes:

[‘NATIVE_EVENTS_ALLOWED’, ‘class’, ‘delattr’, ‘dict’, ‘doc’, ‘format’, ‘getattribute’, ‘hash’, ‘init’, ‘module’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘_file_detector’, ‘_is_remote’, ‘_mobile’, ‘_switch_to’, ‘_unwrap_value’, ‘_wrap_value’, ‘add_cookie’, ‘application_cache’, ‘back’, ‘binary’, ‘capabilities’, ‘close’, ‘command_executor’, ‘create_web_element’, ‘current_url’, ‘current_window_handle’, ‘delete_all_cookies’, ‘delete_cookie’, ‘desired_capabilities’, ‘error_handler’, ‘execute’, ‘execute_async_script’, ‘execute_script’, ‘file_detector’, ‘find_element’, ‘find_element_by_class_name’, ‘find_element_by_css_selector’, ‘find_element_by_id’, ‘find_element_by_link_text’, ‘find_element_by_name’, ‘find_element_by_partial_link_text’, ‘find_element_by_tag_name’, ‘find_element_by_xpath’, ‘find_elements’, ‘find_elements_by_class_name’, ‘find_elements_by_css_selector’, ‘find_elements_by_id’, ‘find_elements_by_link_text’, ‘find_elements_by_name’, ‘find_elements_by_partial_link_text’, ‘find_elements_by_tag_name’, ‘find_elements_by_xpath’, ‘firefox_profile’, ‘forward’, ‘get’, ‘get_cookie’, ‘get_cookies’, ‘get_log’, ‘get_screenshot_as_base64’, ‘get_screenshot_as_file’, ‘get_screenshot_as_png’, ‘get_window_position’, ‘get_window_size’, ‘implicitly_wait’, ‘log_types’, ‘maximize_window’, ‘mobile’, ‘name’, ‘orientation’, ‘page_source’, ‘profile’, ‘quit’, ‘refresh’, ‘save_screenshot’, ‘session_id’, ‘set_page_load_timeout’, ‘set_script_timeout’, ‘set_window_position’, ‘set_window_size’, ‘start_client’, ‘start_session’, ‘stop_client’, ‘switch_to’, ‘switch_to_active_element’, ‘switch_to_alert’, ‘switch_to_default_content’, ‘switch_to_frame’, ‘switch_to_window’, ‘title’, ‘w3c’, ‘window_handles’]

调用说明:

driver. 属性值

变量说明:
1.driver.current_url:用于获得当前页面的 URL

2.driver.title:用于获取当前页面的标题

3.driver.page_source: 用于获取页面 html 源代码

4.driver.current_window_handle: 用于获取当前窗口句柄

5.driver.window_handles: 用于获取所有窗口句柄

函数说明:

1.driver.find_element*(): 定位元素,
2.driver.get(url): 浏览器加载 url。
实例:driver.get(“http//:www.baidu.com”)
3.driver.forward():浏览器向前(点击向前按钮)。
4.driver.back():浏览器向后(点击向后按钮)。
5.driver.refresh():浏览器刷新(点击刷新按钮)。
6.driver.close():关闭当前窗口,或最后打开的窗口。
7.driver.quit(): 关闭所有关联窗口,并且安全关闭 session。

8.driver.maximize_window(): 最大化浏览器窗口。
9.driver.set_window_size(宽,高):设置浏览器窗口大小。
10.driver.get_window_size():获取当前窗口的长和宽。
11.driver.get_window_position():获取当前窗口坐标。
12.driver.get_screenshot_as_file(filename): 截取当前窗口。

实例:driver.get_screenshot_as_file(‘D:/selenium/image/baidu.jpg’)

13.driver.implicitly_wait(秒):隐式等待,通过一定的时长等待页面上某一元素加载完成。
若提前定位到元素,则继续执行。若超过时间未加载出,则抛出 NoSuchElementException 异常。

实例:driver.implicitly_wait(10) #等待 10 秒

14.driver.switch_to_frame(id 或 name 属性值):切换到新表单 (同一窗口)。若无 id 或属性值,可先通过 xpath 定位到 iframe,再将值传给 switch_to_frame()

15.driver.switch_to.parent_content(): 跳出当前一级表单。该方法默认对应于离它最近的 switch_to.frame() 方法。

16.driver.switch_to.default_content(): 跳回最外层的页面。
17.driver.switch_to_window(窗口句柄):切换到新窗口。
18.driver.switch_to.window(窗口句柄): 切换到新窗口。
19.driver.switch_to_alert(): 警告框处理。处理 JavaScript 所生成的 alert,confirm,prompt.

20.driver.switch_to.alert(): 警告框处理。
21.driver.execute_script(js): 调用 js。

22.driver.get_cookies(): 获取当前会话所有 cookie 信息。

23.driver.get_cookie(cookie_name):返回字典的 key 为“cookie_name”的 cookie 信息。

实例:driver.get_cookie(“NET_SessionId”)

24.driver.add_cookie(cookie_dict): 添加 cookie。“cookie_dict”指字典对象,必须有 name 和 value 值。
25.driver.delete_cookie(name,optionsString): 删除 cookie 信息。

26.driver.delete_all_cookies(): 删除所有 cookie 信息。

页面元素属性:

WebElement attributes:

[‘class’, ‘delattr’, ‘dict’, ‘doc’, ‘eq’, ‘format’, ‘getattribute’, ‘hash’, ‘init’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘_execute’, ‘_id’, ‘_parent’, ‘_upload’, ‘_w3c’, ‘clear’, ‘click’, ‘find_element’, ‘find_element_by_class_name’, ‘find_element_by_css_selector’, ‘find_element_by_id’, ‘find_element_by_link_text’, ‘find_element_by_name’, ‘find_element_by_partial_link_text’, ‘find_element_by_tag_name’, ‘find_element_by_xpath’, ‘find_elements’, ‘find_elements_by_class_name’, ‘find_elements_by_css_selector’, ‘find_elements_by_id’, ‘find_elements_by_link_text’, ‘find_elements_by_name’, ‘find_elements_by_partial_link_text’, ‘find_elements_by_tag_name’, ‘find_elements_by_xpath’, ‘get_attribute’, ‘id’, ‘is_displayed’, ‘is_enabled’, ‘is_selected’, ‘location’, ‘location_once_scrolled_into_view’, ‘parent’, ‘rect’, ‘screenshot’, ‘screenshot_as_base64’, ‘screenshot_as_png’, ‘send_keys’, ‘size’, ‘submit’, ‘tag_name’, ‘text’, ‘value_of_css_property’]

调用说明:
driver.find_element*. 属性值

element=driver.find_element*
element. 属性值

变量说明:
1.element.size: 获取元素的尺寸。
2.element.text:获取元素的文本。
3.element.tag_name: 获取标签名称。

函数说明:
1.element.clear(): 清除文本。
2.element.send_keys(value): 输入文字或键盘按键(需导入 Keys 模块)。
3.element.click():单击元素。
4.element.get_attribute(name): 获得属性值
5.element.is_displayed(): 返回元素结果是否可见(True 或 False)
6.element.is_selected(): 返回元素结果是否被选中(True 或 False)
7.element.find_element*(): 定位元素,用于二次定位。

网页自动化最基本的要求就是要定位到各个元素,然后才能对该元素进行各种操作(输入,点击,清除,提交等)。

以百度搜索输入框为例,具体说明各个定位方式的用法:

(通过 chrome 浏览器查看元素或者搜狐浏览器的 firebug 查看,即可看到 html 源码)

注意点:第三行的元素是灰色的,该元素是不可定位到的,下方会说明。

1
2
3
4
5
6
7
8

1. 通过 id 定位元素

如果 id 不是动态的,一个页面的 id 是唯一的。最简单的定位方式。

使用:find_element_by_id(“id_vaule”)

实例:find_element_by_id(“kw”)

注意点:有些 id 值是动态变化的,则不能使用该方法定位。如下:id 就是动态的,每次进入页面,该 id 都会改变

邮箱帐号或手机号

2. 通过 class_name 定位元素

classname 有可能重复哦。

使用:find_element_by_class_name(“class_name_vaule”)

实例:find_element_by_class_name(“s_ipt”)

3. 通过 tag_name 定位元素

标签名字最容易重复,不过,当定位一组数据时,可使用。

使用:find_element_by_tag_name(“tag_name_vaule”)

实例:find_element_by_tag_name(“input”)

注意点:当定位一组元素时:可勾选一组复选框。如下:

find_element_by_tag_name(“input”)

checkbox
checkbox1
checkbox2
checkbox3

4. 通过 name 定位元素

name 有可能会重复哦。

使用:find_element_by_name(“name_vaule”)

实例:find_element_by_name(“wd”)

5. 通过 link 文字精确定位元素

登录

使用:find_element_by_link_text(“text_vaule”)

实例:find_element_by_link_text(“登录”)

6. 通过 link 文字模糊定位元素

使用:find_element_by_partial_link_text(“部分 text_vaule”)

实例:find_element_by_partial_link_text(“登”)

7. 通过 CSS 定位元素

CSS(Cascading Style Sheets)是一种语言,它用来描述 HTML 和 XML 文档的表现。CSS 可以较为灵活的选择控件的任意属性,一般情况下会比 XPath 快。且语法也比较简洁。


使用:find_element_by_css_selector(“CSS”)

实例:

7.1 通过 id 属性定位元素

#号表示通过 id 属性来定位元素

find_element_by_css_selector(“#kw”)

7.2 通过 class 属性定位元素

. 号表示通过 class 属性来定位元素

find_element_by_css_selector(“.s_ipt”)

7.3 通过标签名定位元素

find_element_by_css_selector(“input”)

7.4 通过属性定位元素(挺常用的)

find_element_by_css_selector(“[name=‘wd’]”)

find_element_by_css_selector(“[maxlength=‘255’]”)

属性值包含某个值

属性值包含 wd:适用于由空格分隔的属性值。

find_element_by_css_selector(“[name~=‘wd’]”)

7.5 父子定位元素

查找有父亲元素的标签名为 span,它的所有标签名叫 input 的子元素

**find_element_by_css_selector(“span>input”) **

7.6 组合定位元素

标签名 #id 属性值:指的是该 input 标签下 id 属性为 kw 的元素

find_element_by_css_selector(“input#kw”)

标签名.class 属性值:指的是该 input 标签下 class 属性为 s_ipt 的元素

find_element_by_css_selector(“input.s_ipt”)

标签名 [属性 =’属性值‘]:指的是该 input 标签下 name 属性为 wd 的元素

find_element_by_css_selector(“input[name=‘wd’]”)

父元素标签名 > 标签名.class 属性值:指的是 span 下的 input 标签下 class 属性为 s_ipt 的元素

find_element_by_css_selector(“span>input.s_ipt”)

多个属性组合定位元素(挺常用的)

指的是 input 标签下 id 属性为 kw 且 name 属性为 wd 的元素

find_element_by_css_selector(“input.s_ipt[name=‘wd’]”)

指的是 input 标签下 name 属性为 wd 且 maxlength 为 255 的元素

find_element_by_css_selector(“input[name=‘wd’][maxlength='255']”)

8. 通过 XPath 定位元素

XPath 是一种 XML 文档中定位元素的语言。该定位方式也是比较常用的定位方式。

使用:find_element_by_xpath(“XPath”)

8.1 通过属性定位元素

find_element_by_xpath(“// 标签名 [@属性 =‘属性值’]”)

id 属性:find_element_by_xpath(“//input[@id=‘kw’]”)

class 属性:find_element_by_xpath(“//input[@class=‘s_ipt’]”)

name 属性:find_element_by_xpath(“//input[@name=‘wd’]”)

maxlength 属性:find_element_by_xpath(“//input[@maxlength=‘255’]”)

8.2 通过标签名定位元素

指所有 input 标签元素 find_element_by_xpath(“//input”)

8.3 父子定位元素

查找有父亲元素的标签名为 span,它的所有标签名叫 input 的子元素

find_element_by_xpath(“//span/input”)

8.4 根据元素内容定位元素(非常实用)

find_element_by_xpath(“//p[contains(text(),‘京公网’)]”)

京公网安备 11000002000001 号

注:contains 的另一种用法

//**input[contains(@class,‘s’)] **说明 class 属性包含 s 的元素

8.5 组合定位元素

// 父元素标签名 / 标签名的属性值:指的是 span 下的 input 标签下 class 属性为 s_ipt 的元素

find_element_by_xpath(“//span/input[@class=‘s_ipt’]”)

多个属性组合定位(挺常用的)

指的是 input 标签下 id 属性为 kw 且 name 属性为 wd 的元素

find_element_by_xpath(“//input[@class=‘s_ipt’ and @name=‘wd’]”)

指的是 p 标签下内容包含“京公网”且 id 属性为 jgwab 的元素

find_element_by_xpath(“//p[contains(text(),‘京公网’)and @id=‘jgwab’]”)

比较懒惰的方法:

使用搜狐浏览器的 firebug 工具,复制 XPath 路径,不过这种方式对层级要求高,到时候自己再修改下。

9. 通过 By 定位元素

使用:find_element(定位的类型,具体定位方式)

定位的类型包括 By.ID,By.NAME,By.CLASS_NAME,By.TAG_NAME,By.LINK_TEXT,By.PARTIAL_LINK_TEXT,By.XPATH,By.CSS_SELECTOR

具体定位方式参考上方 1-8 的说明。

实例:find_element(By.ID,‘kw’)

注意:使用 By 定位方式,需先导入 By 类

from selenium.webdriver.common.by import By

10. 具体实例说明

下方例子是登陆 126 邮件,然后发送邮件。

1 # coding=utf-8
2 '''
3 Created on 2016-7-27
4 @author: Jennifer
5 Project: 发送邮件
6 '''
7 from selenium import webdriver
8 import time
9
10 from test_5_2_public import Login # 由于公共模块文件命名为 test_5_2_public
11 driver=webdriver.Firefox()
12 driver.implicitly_wait(30)
13 driver.get(r’http://www.126.com/‘) # 字符串加 r,防止转义。
14 time.sleep(3)
15 driver.switch_to.frame(‘x-URS-iframe’)
16 #调用登录模块
17 Login().user_login(driver)
18 time.sleep(10)
19 #发送邮件
20 #点击发件箱
21 #_mail_component_61_61 是动态 id,所以不能用于定位
22 #driver.find_element_by_css_selector(’#_mail_component_61_61>span.oz0’).click()
23 #不能加 u"//span[contains(text(),u’写 信’)]“,否则定位不到。
24 #以下定位是查找 span 标签有个文本(text)包含(contains)‘写 信’ 的元素,该定位方法重要
25 **driver.find_element_by_xpath(”//span[contains(text(),‘写 信’)]“).click()**
26 #填写收件人
27 #driver.find_element_by_class_name(‘nui-editableAddr-ipt’).send_keys(r’xxx@doov.com.cn’)
28 driver.find_element_by_class_name(‘nui-editableAddr-ipt’).send_keys(r’xxx@163.com’)
29 #填写主题
30 #通过 and 连接更多的属性来唯一地标志一个元素
31 **driver.find_element_by_xpath(”//input[@class=‘nui-ipt-input’ and @maxlength=‘256’]“).send_keys(u’自动化测试’)**
32 #填写正文
33 #通过 switch_to_frame() 将当前定位切换到 frame/iframe 表单的内嵌页面中
34 driver.switch_to_frame(driver.find_element_by_class_name(‘APP-editor-iframe’))
35 #在内嵌页面中定位邮件内容位置
36 emailcontext=driver.find_element_by_class_name(‘nui-scroll’)
37 #填写邮件内容
38** emailcontext.send_keys(u’这是第一封自动化测试邮件’)**
39 #通过 switch_to().default_content() 跳回最外层的页面
40 #注:不要写成 switch_to().default_content(), 否则报 AttributeError: SwitchTo instance has no call method
41 driver.switch_to.default_content()
42 #driver.switch_to.parent_frame()
43 #点击发送
44 time.sleep(3)
45 #有可能存在元素不可见(查看元素是灰色的),会报 ElementNotVisibleException 错误
46 #包含发送二字的元素很多,所以还得再加上其他限制
47 #sendemails=driver.find_element_by_xpath(”//span[contains(text(),‘发送’)]“)
48 **sendemails=driver.find_element_by_xpath(”//span[contains(text(),‘发送’)and @class=‘nui-btn-text’]")**
49 time.sleep(3)
50
51 #校验邮件是否发送成功
52 try:
53 assert ‘发送成功’ in driver.page_source
54 except AssertionError:
55 print ‘邮件发送成功’
56 else:
57 print ‘邮件发送失败’
58
59 #调用退出模块
60 Login().user_logout(driver)

元素定位说明:
1. 代码 22 行,定位不到是因为 id 是动态的,所以需采取其他方式定位元素。

2. 代码 25 行,是根据元素内容来定位的,具体用法详看 8.4.

3. 代码 28 行,是根据 class 名来定位元素的,由于该值在该页面上是唯一的,所以可以用它来定位。

4. 代码 31 行,是使用逻辑运算符 and 连接更多的属性从而唯一的标志一个元素,具体用法详看 8.5.

5. 代码 34 行,由于使用内嵌的 iframe 框架,所以需要先使用 switch_to_frame() 移到该表单上,才能定位该表单上的元素,非常重要,否则无论怎么定位都会报“NoSuchElementException”,找不到该元素。

6. 代码 41 行,跳出 iframe 框架,当框架内的动作操作完毕后,需要使用 switch_to.default_content 跳出 iframe 框架, 非常重要。

7. 代码 47 行,由于内容包括“发送”的元素中包含不可见元素(html 查看元素可以看到此行是灰色的),这样有可能定位到不可见元素,会报“ElementNotVisibleException”。

8. 代码 48 行,是使用逻辑运算符 and 连接更多的属性从而唯一的标志一个元素,具体用法详看 8.5. 这样可以排除掉那个不可见元素。

1.Frame/Iframe 原因定位不到元素:
  这个是最常见的原因,首先要理解下 frame 的实质,frame 中实际上是嵌入了另一个页面,而 webdriver 每次只能在一个页面识别,因此需要先定位到相应的 frame,对那个页面里的元素进行定位。

解决方案:

如果 iframe 有 name 或 id 的话,直接使用 switch_to_frame(“name 值”) 或 switch_to_frame(“id 值”)

​如下:
driver=webdriver.Firefox()
driver.get(r’http://www.126.com/’)
driver.switch_to_frame(‘x-URS-iframe’) # 需先跳转到 iframe 框架
username=driver.find_element_by_name(‘email’)
username.clear()

如果 iframe 没有 name 或 id 的话,则可以通过下面的方式定位:
#先定位到 iframe
elementi= driver.find_element_by_class_name(‘APP-editor-iframe’)
#再将定位对象传给 switch_to_frame() 方法
driver.switch_to_frame(elementi)

如果完成操作后,
可以通过 switch_to.parent_content()方法跳出当前 iframe,或者还可以通过 switch_to.default_content() 方法跳回最外层的页面。


2.Xpath 描述错误原因:

由于 Xpath 层级太复杂,容易犯错。但是该定位方式能够有效定位绝大部分的元素,建议掌握。
解决方案:

2.1 可以使用 Firefox 的 firePath,复制 xpath 路径。该方式容易因为层级改变而需要重新编写过 xpath 路径,不建议使用,初学者可以先复制路径,然后尝试去修改它。

2.2 提高下写 xpath 的水平。


如何检验编写的 Xpath 是否正确?编写好 Xpath 路径,可以直接复制到搜狐浏览器的 firebug 查看 html 源码,通过 Xpath 搜索:如下红色框,若无报错,则说明编写的 Xpath 路径没错。

**find_element_by_xpath(“//input[@id=‘kw’]”) **

​3. 页面还没有加载出来,就对页面上的元素进行的操作:
这种情况一般说来,可以设置等待,等待页面显示之后再操作,这与人手工操作的原理一样:
3.1 设置等待时间;缺点是需要设置较长的等待时间,案例多了测试就很慢;
3.2 设置等待页面的某个元素出现,比如一个文本、一个输入框都可以,一旦指定的元素出现,就可以做操作。
3.3 在调试的过程中可以把页面的 html 代码打印出来,以便分析。

解决方案:
导入时间模块。
import time
time.sleep(3)

4. 动态 id 定位不到元素:
解决方案:
如果发现是动态 id,直接用 xpath 定位或其他方式定位。

​​

5. 二次定位,如弹出框登录

如百度登录弹出框登录百度账号,需先定位到百度弹出框,然后再定位到用户名密码登录。

#coding=utf-8
'''
Created on 2016-7-20
@author: Jennifer
Project: 登录百度账号
'''
from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(“http://www.baidu.com/”)

time.sleep(3)

#点击登录:有些 name 为 tj_login 的元素为不可见的,点击可见的那个登录按钮即可。
#否则会报:ElementNotVisibleException
element0=driver.find_elements_by_name(“tj_login”)
for ele0 in element0:
if ele0.is_displayed():
ele0.click()

#在登录弹出框,需先定位到登录弹出框
#否则会报:NoSuchElementException
element1=driver.find_element_by_class_name(“tang-content”)
element11=element1.find_element_by_id(“TANGRAM__PSP_8__userName”)
element11.clear()
element11.send_keys(“登录名”)

element2=element1.find_element_by_id(“TANGRAM__PSP_8__password”)
element2.clear()
element2.send_keys(“密码”)

element3=element1.find_element_by_id(“TANGRAM__PSP_8__submit”)
element3.click()
element3.submit()

try:
assert “登录名” in driver.page_source
except AssertionError:
print “登录失败”
else:
print “登录成功”
time.sleep(3)
finally:
print “测试记录:已测试”
driver.close()

6. 不可见元素定位

如上百度登录代码,通过名称为 tj_login 查找的登录元素,有些是不可见的,所以加一个循环判断,找到可见元素(is_displayed())点击登录即可。

. 事件委托

什么是事件委托?用现实中的理解就是:有100 个学生同时在某天中午收到快递,但这

100 个学生不可能同时站在学校门口等,那么都会委托门卫去收取,然后再逐个交给学生。

而在jQuery 中,我们通过事件冒泡的特性,让子元素绑定的事件冒泡到父元素(或祖先元素)

上,然后再进行相关处理即可。

如果一个企业级应用做报表处理,表格有2000 行,每一行都有一个按钮处理。如果用

之前的.bind()处理,那么就需要绑定2000 个事件,就好比2000 个学生同时站在学校门口等

快递,不断会堵塞路口,还会发生各种意外。这种情况放到页面上也是一样,可能导致页面

极度变慢或直接异常。而且,2000 个按钮使用ajax 分页的话,.bind()方法无法动态绑定尚

未存在的元素。就好比,新转学的学生,快递员无法验证他的身份,就可能收不到快递。

//HTML 部分

<div style="background:red;width:200px;height:200px;" id="box">

<input type="button" value="按钮" class="button" />

</div>

//使用.bind()不具备动态绑定功能,只有点击原始按钮才能生成

$('.button').bind('click', function () {

$(this).clone().appendTo('#box');

});

//使用.live()具备动态绑定功能,jQuery1.3 使用,jQuery1.7 之后废弃,jQuery1.9 删除

$('.button').live('click', function () {

$(this).clone().appendTo('#box');

});

.live()原理就是把click 事件绑定到祖先元素$(document)上,而只需要给$(document)绑

定一次即可,而非2000 次。然后就可以处理后续动态加载的按钮的单击事件。在接受任何

事件时,$(document)对象都会检查事件类型(event.type)和事件目标(event.target),如果click

事件是.button,那么就执行委托给它的处理程序。.live()方法已经被删除,无法使用了。需

要测试使用的话,需要引入向下兼容插件。

//.live()无法使用链接连缀调用,因为参数的特性导致

$('#box').children(0).live('click', function () {

$(this).clone().appendTo('#box');

});

在上面的例子中,我们使用了.clone()克隆。其实如果想把事件行为复制过来,我们只

需要传递true 即可:.clone(true)。这样也能实现类似事件委托的功能,但原理却截然不同。

一个是复制事件行为,一个是事件委托。而在非克隆操作下,此类功能只能使用事件委托。

$('.button').live('click', function () {

$('<input type="button" value="复制的" class="button" />').appendTo('#box');

});

当我们需要停止事件委托的时候,可以使用.die()来取消掉。

$('.button').die('click');

由于.live()和.die()在jQuery1.4.3 版本中废弃了,之后推出语义清晰、减少冒泡传播层次、

又支持链接连缀调用方式的方法:.delegate()和.undelegate()。但这个方法在jQuery1.7 版本中

被.on()方法整合替代了。

$('#box').delegate('.button', 'click', function () {

$(this).clone().appendTo('#box');

});

$('#box').undelegate('.button','click');

//支持连缀调用方式

$('div').first().delegate('.button', 'click', function () {

$(this).clone().appendTo('div:first');

});

注意:.delegate()需要指定父元素,然后第一个参数是当前元素,第二个参数是事件方

式,第三个参数是执行函数。和.bind()方法一样,可以传递额外参数。.undelegate()和.unbind()

方法一样可以直接删除所有事件,比如:.undelegate('click')。也可以删除命名空间的事件,

比如:.undelegate('click.abc')。

注意:.live()和.delegate()和.bind()方法一样都是事件绑定,那么区别也很明显,用途上

遵循两个规则:1.在DOM 中很多元素绑定相同事件时;2.在DOM 中尚不存在即将生成的

元素绑定事件时;我们推荐使用事件委托的绑定方式,否则推荐使用.bind()的普通绑定。

二.on、off 和one

目前绑定事件和解绑的方法有三组共六个。由于这三组的共存可能会造成一定的混乱,

为此jQuery1.7 以后推出了.on()和.off()方法彻底摒弃前面三组。

//替代.bind()方式

$('.button').on('click', function () {

alert('替代.bind()');

});

//替代.bind()方式,并使用额外数据和事件对象

$('.button').on('click', {user : 'Lee'}, function (e) {

alert('替代.bind()' + e.data.user);

});

//替代.bind()方式,并绑定多个事件

$('.button').on('mouseover mouseout', function () {

alert('替代.bind()移入移出!');

});

//替代.bind()方式,以对象模式绑定多个事件

$('.button').on({

mouseover : function () {

alert('替代.bind()移入!');

},

mouseout : function () {

alert('替代.bind()移出!');

}

});

//替代.bind()方式,阻止默认行为并取消冒泡

$('form').on('submit', function () {

return false;

});

$('form').on('submit', false);

//替代.bind()方式,阻止默认行为

$('form').on('submit', function (e) {

e.preventDefault();

});

//替代.bind()方式,取消冒泡

$('form').on('submit', function (e) {

e.stopPropagation();

});

//替代.unbind()方式,移除事件

$('.button').off('click');

$('.button').off('click', fn);

$('.button').off('click.abc');

//替代.live()和.delegate(),事件委托

$('#box').on('click', '.button', function () {

$(this).clone().appendTo('#box');

});

//替代.die()和.undelegate(),取消事件委托

$('#box').off('click', '.button');

注意:和之前方式一样,事件委托和取消事件委托也有各种搭配方式,比如额外数据、

命名空间等等,这里不在赘述。

不管是.bind()还是.on(),绑定事件后都不是自动移除事件的,需要通过.unbind()和.off()

来手工移除。jQuery 提供了.one()方法,绑定元素执行完毕后自动移除事件,可以方法仅触

发一次的事件。

//类似于.bind()只触发一次

$('.button').one('click', function () {

alert('one 仅触发一次!');

});

//类似于.delegate()只触发一次

$('#box).one('click', 'click', function () {

alert('one 仅触发一次!');

});

三:例子:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <script src="js/jquery-1.8.3.js"></script>
 <script>
 $(function () {
 // 动态添加的不起作用
 $(".me1").click(function () {
 alert("jQuery click绑定事件");
 });
 // 动态添加的不起作用
 $(".me2").bind('click', function () {
 alert("jQuery bind绑定事件");
 });
 // 动态添加的起作用, jquery 1.9on取代live
 $('.me3').live('click', function () {
// $('.me3').on('click', function () {
 alert("jQuery live绑定事件.");
 });
 $('.me4').on('click', function () {
 alert("jQuery on绑定事件.");
 });
 //动态添加的不起作用
 $('#live').live('click', function () {
 alert("事件委托");
 });
 });
 </script>
 <title>Title</title>
</head>
<body>
<input type="button" value="onclick" class="me1">
<input type="button" value="bind" class="me2">
<input type="button" value="live" class="me3">
<input type="button" value="on" class="me4">
<input type="button" value="增加标签" class="me11" onclick="addOne()">
<div id="live">
 <p>AAA</p>
 <p>AAA</p>
 <p>AAA</p>
 <p>AAA</p>
 <p>AAA</p>
</div>
<script>
 function addOne() {
 $('body').append($('<br/><input type="button" value="动态加的onclick不起作用" class="me1">'));
 $('body').append($('<input type="button" value="动态加的bind不起作用" class="me2">'));
 $('body').append($('<input type="button" value="动态加的live不起作用" class="me3">'));
 $('body').append($('<input type="button" value="动态加的on不起作用" class="me4">'));
 }
</script>
</body>
</html>

结果:红线圈的点击可以弹框

改进:使on可以委托(js加的也起作用)

先将事件绑定到body身上再过滤到.me4(也就是事件在class为me4身上)

 $('body').on('click','.me4', function () {
 alert("jQuery on绑定事件.");
 });

改进:on绑定多个事件并进行委托:

参数中写多个事件,根据事件类型进行判断(下面效果是实现hover()函数)

 $("body").on("mouseover mouseout",'.el_unflod',function(event){
 if(event.type == "mouseover"){
 // 鼠标悬浮
 alert("鼠标悬停")
 }else if(event.type == "mouseout"){
 // 鼠标离开
 }
})

四:总结

bind:绑定事件,页面初始化绑定,可以同时绑定多个,JS动态添加的元素不起作用。而且用unbind()去掉事件只能去掉bind绑定的事件,对于标签种写的onclick属性之类的事件需要调用removeAttr删除属性才可以去掉事件,切记在bind事件之前去掉元素的onclick属性等事件。

onclick: 绑定事件,页面初始化绑定,一次绑定一个,JS动态添加的元素不起作用,bind的简便写法

live: 事件委托,也是事件绑定,JS动态加入的元素仍然起作用(事件委托:动态添加的元素仍然有事件)

on: 绑定事件,也是绑定事件(功能更强大),运用上述方法可以实现委托,动态添加的元素仍然有事件