selenium

 ·  ☕ 7  · 👀...

介绍

技术 特性 适用场景 说明
Selenium - 数据爬取
- 自动化测试
- 官网
- Doc
- Getting started

基础

元素定位

假如我们有一个Web页面,通过前端工具(如,Firebug)查看到一个元素的属性是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
  <head>
  <body link="#0000cc">
    <a id="result_logo" href="/" onmousedown="return c({'fm':'tab','tab':'logo'})">
    <form id="form" class="fm" name="f" action="/s">
      <span class="soutu-btn"></span>
        <input id="kw" class="s_ipt" name="wd" value="" maxlength="255" autocomplete="off">

<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
<a class="mnav" href="http://www.hao123.com" name="tj_trhao123">hao123</a>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 定位 input 标签的输入框
## 通过id定位:
$ dr.find_element_by_id("kw")

## 通过name定位:
$ dr.find_element_by_name("wd")

## 通过class name定位:
$ dr.find_element_by_class_name("s_ipt")

## 通过tag name定位:
$ dr.find_element_by_tag_name("input")

## 通过xpath定位,xpath定位有N种写法,这里列几个常用写法:
$ dr.find_element_by_xpath("//*[@id='kw']")
$ dr.find_element_by_xpath("//*[@name='wd']")
$ dr.find_element_by_xpath("//input[@class='s_ipt']")
$ dr.find_element_by_xpath("/html/body/form/span/input")
$ dr.find_element_by_xpath("//span[@class='soutu-btn']/input")
$ dr.find_element_by_xpath("//form[@id='form']/span/input")
$ dr.find_element_by_xpath("//input[@id='kw' and @name='wd']")

## 通过css定位,css定位有N种写法,这里列几个常用写法:
$ dr.find_element_by_css_selector("#kw")
$ dr.find_element_by_css_selector("[name=wd]")
$ dr.find_element_by_css_selector(".s_ipt")
$ dr.find_element_by_css_selector("html > body > form > span > input")
$ dr.find_element_by_css_selector("span.soutu-btn> input#kw")
$ dr.find_element_by_css_selector("form#form > span > input")

# 定位文本连接
## 通过link text定位:
$ dr.find_element_by_link_text("新闻")
$ dr.find_element_by_link_text("hao123")

## 通过link text定位:
$ dr.find_element_by_partial_link_text("新")
$ dr.find_element_by_partial_link_text("hao")
$ dr.find_element_by_partial_link_text("123")

控制浏览器操作

控制浏览器窗口大小

1
2
3
4
5
6
7
8
9
from selenium import webdriver

driver = webdriver.Firefox()
driver.get("http://m.baidu.com")

# 参数数字为像素点
print("设置浏览器宽480、高800显示")
driver.set_window_size(480, 800)
driver.quit()

控制浏览器后退、前进

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from selenium import webdriver

driver = webdriver.Firefox()

#访问百度首页
first_url= 'http://www.baidu.com'
print("now access %s" %(first_url))
driver.get(first_url)

#访问新闻页面
second_url='http://news.baidu.com'
print("now access %s" %(second_url))
driver.get(second_url)

#返回(后退)到百度首页
print("back to  %s "%(first_url))
driver.back()

#前进到新闻页
print("forward to  %s"%(second_url))
driver.forward()

driver.quit()

刷新页面

1
driver.refresh() #刷新当前页面

常用方法

点击和输入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")

# 清除文本
driver.find_element_by_id("kw").clear()

# 模拟按键输入
driver.find_element_by_id("kw").send_keys("selenium")

# 单击元素
driver.find_element_by_id("su").click()

driver.quit()

提交

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")

search_text = driver.find_element_by_id('kw')
search_text.send_keys('selenium')
# submit()方法用于提交表单
search_text.submit()

driver.quit()

其它

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://www.baidu.com")

# 获得输入框的尺寸
size = driver.find_element_by_id('kw').size
print(size)

# 返回百度页面底部备案信息
text = driver.find_element_by_id("cp").text
print(text)

# 返回元素的属性值, 可以是 id、 name、 type 或其他任意属性
attribute = driver.find_element_by_id("kw").get_attribute('type')
print(attribute)

# 返回元素的结果是否可见, 返回结果为 True 或 False
result = driver.find_element_by_id("kw").is_displayed()
print(result)

driver.quit()

鼠标事件

在 WebDriver 中, 将这些关于鼠标操作的方法封装在 ActionChains 类提供。
ActionChains 类提供了鼠标操作的常用方法:

  • perform(): 执行所有 ActionChains 中存储的行为
  • context_click(): 右击
  • double_click(): 双击
  • drag_and_drop(): 拖动
  • move_to_element(): 鼠标悬停

ActionChains(driver)
调用 ActionChains()类, 将浏览器驱动 driver 作为参数传入。

move_to_element(above)
context_click()方法用于模拟鼠标右键操作, 在调用时需要指定元素定位。

perform()
执行所有 ActionChains 中存储的行为, 可以理解成是对整个操作的提交动作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from selenium import webdriver
# 引入 ActionChains 类
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://www.baidu.cn")

# 定位到要悬停的元素
above = driver.find_element_by_link_text("设置")
# 对定位到的元素执行鼠标悬停操作
ActionChains(driver).move_to_element(above).perform()

键盘事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from selenium import webdriver
# 引入 Keys 模块
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()
driver.get("http://www.baidu.com")

# 输入框输入内容
driver.find_element_by_id("kw").send_keys("seleniumm")

# 删除多输入的一个 m
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)

# 输入空格键+“教程”
driver.find_element_by_id("kw").send_keys(Keys.SPACE)
driver.find_element_by_id("kw").send_keys("教程")

# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')

# ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x')

# ctrl+v 粘贴内容到输入框
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'v')

# 通过回车键来代替单击操作
driver.find_element_by_id("su").send_keys(Keys.ENTER)
driver.quit()

设置元素等待

显式等待

显式等待使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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.Firefox()
driver.get("http://www.baidu.com")

element = WebDriverWait(driver, 5, 0.5).until(
                      EC.presence_of_element_located((By.ID, "kw"))
                      )
element.send_keys('selenium')
driver.quit()

隐式等待

WebDriver提供了implicitly_wait()方法来实现隐式等待,默认设置为0。它的用法相对来说要简单得多。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from time import ctime

driver = webdriver.Firefox()

# 设置隐式等待为10秒
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")

try:
    print(ctime())
    driver.find_element_by_id("kw22").send_keys('selenium')
except NoSuchElementException as e:
    print(e)
finally:
    print(ctime())
    driver.quit()

定位一组元素

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from selenium import webdriver
from time import sleep

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")

driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
sleep(1)

# 定位一组元素
texts = driver.find_elements_by_xpath('//div/h3/a')

# 循环遍历出每一条搜索结果的标题
for t in texts:
    print(t.text)

driver.quit()

多表单切换

1
2
3
4
5
6
7
8
#先通过xpth定位到iframe
xf = driver.find_element_by_xpath('//*[@id="x-URS-iframe"]')

#再将定位对象传给switch_to.frame()方法。
## switch_to.frame() 默认可以直接取表单的id 或name属性。
driver.switch_to.frame(xf)
……
driver.switch_to.parent_frame()

多窗口切换

  • current_window_handle:获得当前窗口句柄。
  • window_handles:返回所有窗口的句柄到当前会话。
  • switch_to.window():用于切换到相应的窗口,与上一节的switch_to.frame()类似,前者用于不同窗口的切换,后者用于不同表单之间的切换。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")

# 获得百度搜索窗口句柄
sreach_windows = driver.current_window_handle

driver.find_element_by_link_text('登录').click()
driver.find_element_by_link_text("立即注册").click()

# 获得当前所有打开的窗口的句柄
all_handles = driver.window_handles

# 进入注册窗口
for handle in all_handles:
    if handle != sreach_windows:
        driver.switch_to.window(handle)
        print('now register window!')
        driver.find_element_by_name("account").send_keys('username')
        driver.find_element_by_name('password').send_keys('password')
        time.sleep(2)
        # ……


driver.quit()

警告框处理

在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单,具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ send_keys等方法进行操作。

  • text:返回 alert/confirm/prompt 中的文字信息。
  • accept():接受现有警告框。
  • dismiss():解散现有警告框。
  • send_keys(keysToSend):发送文本至警告框。keysToSend:将文本发送至警告框。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time

driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')

# 鼠标悬停至“设置”链接
link = driver.find_element_by_link_text('设置')
ActionChains(driver).move_to_element(link).perform()

# 打开搜索设置
driver.find_element_by_link_text("搜索设置").click()

# 保存设置
driver.find_element_by_class_name("prefpanelgo").click()
time.sleep(2)

# 接受警告框
driver.switch_to.alert.accept()

driver.quit()

下拉框选择

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep

driver = webdriver.Chrome()
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')

# 鼠标悬停至“设置”链接
driver.find_element_by_link_text('设置').click()
sleep(1)
# 打开搜索设置
driver.find_element_by_link_text("搜索设置").click()
sleep(2)

# 搜索结果显示条数
sel = driver.find_element_by_xpath("//select[@id='nr']")
Select(sel).select_by_value('50')  # 显示50条
# ……

driver.quit()

文件上传

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from selenium import webdriver
import os

driver = webdriver.Firefox()
file_path = 'file:///' + os.path.abspath('upfile.html')
driver.get(file_path)

# 定位上传按钮,添加本地文件
driver.find_element_by_name("file").send_keys('D:\\upload_file.txt')

driver.quit()

获取Cookie

1
2
driver.get("https://www.example.com")
driver.get_cookies()

添加Cookie

1
2
3
driver.get("https://chercher.tech")
cookie = {name : foo, value : bar}
driver.add_cookie(cookie)

删除Cookie

1
driver.delete_cookie("foo")

删除所有Cookie

1
driver.delete_all_cookies()

调用 JavaScript 代码

窗口截图

关闭浏览器

常见问题

‘list’ object has no attribute ‘click’

将find_elements_by_xpath改为find_element_by_xpath。

参考


Wanglibing
Wanglibing
Engineer,Lifelong learner