Selenium 自动化工具

Selenium 自动化工具

Yiuhang Chan

简介

概念

Selenium 是一个web自动化工具

作用

  1. 自动化测试
    通过它,我们可以写出自动化程序,模拟浏览器里操作web界面。 比如点击界面按钮,在文本框中输入文字 等操作。
  2. 获取信息
    而且还能从web界面获取信息。 比如招聘网站职位信息,财经网站股票价格信息 等等,然后用程序进行分析处理。

运行环境

Selenium测试直接运行在浏览器中,就好像一个真正的用户在操作一样, 支持大部分主流的浏览器,包括IE(7,8,9,10,11),Firefox,Safari,Chrome,Opera等。

我们可以利用它来模拟用户点击访问网站,绕过一些复杂的认证场景,通过selenium+驱动浏览器这种组合可以直接渲染解析js,绕过大部分的参数构造和反爬。

注意事项

新版本的Selenium已经不在支持phantomjs,原作者也已经放弃维护该项目了。

还有在做爬虫的时候尽量不要用这种方法,Selenium+浏览器的组合速度慢,应付不了数据量比较大的爬取以及并发爬取。并且很吃电脑资源。

基本使用

原理

1
2
3
flowchart TD
A(selenium) --> B(Chrome)
B --> C(Chrome Drive)
1
2
3
# selenium ------http------> 浏览器驱动, 去操作浏览器实现效果 -------> 浏览器驱动 ----------> selenium
# 浏览器驱动相当于 http server
# 浏览器驱动和浏览器版本要一致

注意:每个驱动该对应每个浏览器;有时候浏览器会自动升级,导致浏览器不可用;

安装

selenium安装

终端输入 pip install selenium==4.0.0

1
2
为什么一定要装这个版本 
我们在给图片定位的时候(0,0)坐标点是选在图片的左上角位置的

浏览器驱动安装

chrome驱动下载地址:
https://chromedriver.chromium.org/downloads

xxxxxxxxxx op1=>operation: 1.找到目标数据op2=>operation: 2.分析请求流程op3=>operation: 3.构造http请求op4=>operation: 4.提取数据op5=>operation: 5.数据持久化​op1(right)->op2(right)->op3(right)->op4(right)->op5(right)flowCreated with Raphaël 2.2.01.找到目标数据2.分析请求流程3.构造http请求4.提取数据5.数据持久化

Microsoft Edge WebDriver - Microsoft Edge Developer

所有edge驱动版本

Microsoft Edge - Webdriver (windows.net)

如何禁止更新–参考博客

https://blog.csdn.net/jylsrnzb/article/details/131492090

然后把下载成功的驱动放到python环境的根目录

验证运行是否成功(Chrome)为例

1
2
3
4
5
from selenium import webdriver

wb = webdriver.Chrome()

wb.get('https://www.baidu.com')

运行后成功自动弹出Chrome即可:

元素选取

find_element(s)by_…方法

在一个页面中有很多不同的策略可以定位一个元素。我们可以选择最合适的方法去查找元素。Selenium提供了下列的方法:

注: 其中的element加上一个s,则是对应的多个元素的查找方法

老版本selenium的方法

单个元素查找方法 作用
find_element_by_xpath() 通过Xpath查找
find_element_by_class_name() 通过class属性查找
find_element_by_id() 通过id属性查找
find_element_by_name() 通过name属性进行查找
find_element_by_css_selector() 通过css选择器查找 语法规则
find_element_by_link_text() 通过链接文本查找
find_element_by_partial_link_text() 通过链接文本的部分匹配查找
find_element_by_tag_name() 通过标签名查找

新版本的用法:By对象查找

除了以上的多种查找方式,还有两种私有方法集成了上面的所有的查找方法,让我们更方便的使用

方法 作用
find_element(By.XPATH, ‘//button/span’) 通过Xpath查找一个
find_elements(By.XPATH, ‘//button/span’) 通过Xpath查找多个

其中的第一个参数可以选择使用查找的方法,By.xxx 使用xxx方式解析,解析方法如下:

注:By对象导入: from selenium.webdriver.common.by import By

1
2
3
4
5
6
7
8
ID = "id" 
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"

文本输入,提交

chrome.find_element_by_id("all").send_keys("username")

chrome.find_element_by_id("password").send_keys("password")

chrome.find_element_by_css_selector("submit button").click()

使用案例(以豆瓣为例)

通过分析搜索框元素发现输入的元素内容search_text

通过分析搜索框元素发现提交的元素内容submit

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.common.by import By

wb = webdriver.Edge()

wb.get('https://movie.douban.com/top250')

# 老版本
# wb.find_element_by_xpath('//input[@name="search_text"]').send_keys('周杰伦')
# wb.find_element_by_xpath('//input[@type="submit"]').click()
# 新版本
wb.find_element(by=By.XPATH, value='//input[@name="search_text"]').send_keys('张译') # 等同于输入框输入值
wb.find_element(By.XPATH, value='//div[@class="inp-btn"]/input').click() # 等同于点击搜索

动作切换

窗口切换

用selenium操作浏览器如果需要在打开新的页面,这个时候会有问题,因为我们用selenium操作的是第一个打开的窗口,所以新打开的页面我们是无法去操作的,所以我们要用到切换窗口:即handle切换的方法

方法 作用
js = ‘window.open(“https://www.baidu.com");'chrome.execute_script(js) 打开新标签
window_handles 获取所有页面窗口的句柄
current_window_handle 获取当前页面窗口的句柄
switch_to.window(window_name) 定位页面转到指定的window_name页面

Window_handles的顺序并不是浏览器上标签的顺序,尽量避免多标签操作

窗口切换案例(少量页面)

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

wb = webdriver.Chrome()

wb.get('https://movie.douban.com/top250')

js = 'window.open("https://www.baidu.com")'
wb.execute_script(js) # 执行js代码
# time.sleep(10) # 10s避免闪退展示影响效果

# print(wb.title) # 豆瓣电影 Top 250,焦点在https://movie.douban.com/top250上
# print(wb.window_handles) # ['14DB233ADDD3A1A9EF54DBDFF3412AE0', 'BD2EDCF05D390B30B0F86FA7BE5A4948'] 查看句柄

wb.switch_to.window(wb.window_handles[1]) # 切换句柄到第二个[1]窗口

# print(wb.title) # 百度一下,你就知道 焦点在https://www.baidu.com上

页面切换

在实际的爬虫中,明明定位的路径没问题,这个时候我们可以考虑一下是否是该页面存在frame/iframe/frameset的问题导有时候我们会遇到找不到元素的问题致的定位不到元素。

方法 作用
switch_to.frame(frame_reference) 切到指定frame,可用id或name(str)、index(int)、元素(WebElement)定位
switch_to.parent_frame() 切到父级frame,如果已是主文档,则无效果, 相当于后退回去
switch_to_default_content() 切换到主页面,DOM树最开始的frame

页面切换案例

由于登录的元素后续跟随有时间戳auto-id-1701347680519,因此选用同样是id的id="j-nav-login",保持唯一性

对于弹窗,首先应获取iframe标签,切换句柄

然后根据其按钮所在元素模拟点击

账号密码通过placeholder来进行定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"""
目的: 实现 https://study.163.com/自动登录
"""
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

wb = webdriver.Chrome()
wb.maximize_window() # 全屏,避免元素点击不到

wb.get('https://study.163.com/')

wb.find_element(by=By.XPATH, value='//span[@class="ux-btn th-bk-main ux-btn- ux-btn- ux-modal-btn um-modal-btn_ok th-bk-main"]').click() #点击弹窗
wb.find_element(by=By.ID, value="j-nav-login").click()

time.sleep(2)
# 先获取iframe标签
fr = wb.find_element(by=By.XPATH,value='//iframe[@frameborder="0"]')
# 再切换焦点到iframe上
wb.switch_to.frame(fr)

wb.find_element(by=By.XPATH, value='//input[@placeholder="请输入手机号"]').send_keys('18300000000')
wb.find_element(by=By.XPATH, value='//input[@placeholder="请输入密码"]').send_keys('qwe123...')

页面弹窗

有的时候还会遇到弹窗的问题, 主要有两种一种是浏览器弹窗(alert/prompt),一种是自定义弹窗
自定义弹窗,就是一个自定义的div层,是隐藏页面中的,当触发了这个弹窗后,他就显示出来,这种方式我们通过正常的定位方式是可以定位到的。
alert弹窗,就要用下面的方法处理:

1
2
# 老的版本使用的是switch_to_alert
# 现在使用的是switch_to
方法 作用
switch_to 定位到alert弹窗,返回一个弹窗的对象
dismiss() 对弹窗对象的取消操作(相当于点击弹窗上的取消按钮)
accept() 对弹窗对象的确定操作(相当于点击弹窗上的确定按钮)
send_keys(key) 对弹窗对象内的输入框输入数据(针对于prompt弹窗)
text 获取弹窗内的文本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--第一种-->
<input type="button" id="alert" value="alert" onclick="alert('welcome! 请按确认继续')">
<!--第二种-->
<input type="button" id="confirm" value="confirm" onclick="confirm('确认吗?')">
<!--第三种-->
<input type="button" id="prompt" value="prompt" onclick="var name=prompt('请输入的名字:'); document.write(name)">
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import time

from selenium import webdriver
from selenium.webdriver.common.by import By


wb = webdriver.Chrome()
wb.get(r'\弹框.html')

# 第一种
# wb.find_element(by=By.ID, value='alert').click()
# print(wb.switch_to.alert.text)

# 第二种
# wb.find_element(by=By.ID, value='confirm').click()
# print(wb.switch_to.alert.text)

# 第三种
# wb.find_element(by=By.ID, value='prompt').click()
# prompt_tag = wb.switch_to.alert
# time.sleep(2)
# prompt_tag.send_keys("CosTrace")
# time.sleep(2)
# prompt_tag.accept()

等待

简介

在selenium操作浏览器的过程中,每一次请求url,selenium都会等待页面加载完成以后,才会将操作权限在交给我们的程序。但是,由于ajax和各种JS代码的异步加载问题,当一个页面被加载到浏览器时,该页面内的元素可以在不同的时间点被加载,这就使得元素的定位变得十分困难,当元素不再页面中时,使用selenium去查找的时候会抛出ElementNotVisibleException异常。为了解决这个问题,selenium提供了两种等待页面加载的方式,显示等待和隐式等待,让我们可以等待元素加载完成后在进行操作。

显式等待

显式等待: 显式等待指定某个条件,然后设置最长等待时间,程序每隔XX时间看一眼,如果条件成立,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出超时异常(TimeoutException)。
显示等待主要使用了WebDriverWait类与expected_conditions模块。
一般写法:WebDriverWait(driver, timeout, poll_frequency, igonred_exceptions).until(method, message)
Driver:传入WebDriver实例。
timeout: 超时时间,等待的最长时间(同时要考虑隐性等待时间)
poll_frequency: 调用until中的方法的间隔时间,默认是0.5秒
ignored_exceptions: 忽略的异常,如果在调用until的过程中抛出这个元组中的异常,则不中断代码,继续等待.
Method:可执行方法
Message: 超时时返回的信息

1
2
3
4
5
6
7
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

# 获取等待的元素
element = (By.XPATH, '//iframe[@frameborder="0"]')
# 显式等待
WebDriverWait(driver=wb, timeout=5, poll_frequency=0.2).until(EC.presence_of_element_located(element), 'Not Found')

expected_conditions条件

expected_conditions是selenium的一个子模块,其中包含一系列可用于判断的条件,配合该类的方法,就能够根据条件而进行灵活地等待了

ActionChains提供的方法 作用
title_is title_contains 这两个条件类验证title,验证传入的参数title是否等于或包含于driver
presence_of_element_located presence_of_all_elements_located 这两个条件验证元素是否出现,传入的参数都是元组类型的locator,如(By.ID, ‘kw’)顾名思义,一个只要一个符合条件的元素加载出来就通过;另一个必须所有符合条件的元素都加载出来才行
visibility_of_element_located invisibility_of_element_located visibility_of 这三个条件验证元素是否可见,前两个传入参数是元组类型的locator,第三个传入WebElement
text_to_be_present_in_element text_to_be_present_in_element_value 判断某段文本是否出现在某元素中,一个判断元素的text,一个判断元素的value
frame_to_be_available_and_switch_to_it 判断frame是否可切入,可传入locator元组或者直接传入定位方式:id、name、index或WebElement
alert_is_present 判断是否有alert出现
element_to_be_clickable 判断元素是否可点击,传入locator

隐性等待

隐性等待implicitly_wait(xx) :设置了一个最长等待时间,如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间截止,然后执行下一步。
弊端就是程序会一直等待整个页面加载完成,就算需要的元素加载出来了还是需要等待。也就是一般情况下看到浏览器标签栏那个小圈不再转,才会执行下一步,
隐性等待对整个driver的周期都起作用,所以只要设置一次即可
隐性等待和显性等待可以同时用,但要注意:等待的最长时间取两者之中的大者
默认等待时间为0,可以通过下面的方式设置:

1
2
3
4
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) #隐式等待,最长10s
driver.get('https://www.baidu.com')

强制等待

强制等待就是不论如何,在此处都需要阻塞等待一段时间,及time.sleep()

动作链

在selenium当中除了简单的点击动作外,还有一些稍微复杂的动作,就需要用到ActionChains(动作链)这个子模块来满足我们的需求。

ActionChains可以完成复杂一点的页面交互行为,例如元素的拖拽,鼠标移动,悬停行为,内容菜单交互。它的执行原理就是当调用ActionChains方法的时候不会立即执行,而是将所有的操作暂时储存在一个队列中,当调用perform()方法的时候,会按照队列中放入的先后顺序执行前面的操作。

ActionChains包:from selenium.webdriver.common.action_chains import ActionChains

方法

ActionChains提供的方法 作用
click(on_element=None) 鼠标左键单击传入的元素
double_click(on_element=None) 双击鼠标左键
context_click(on_element=None) 点击鼠标右键
click_and_hold(on_element=None) 点击鼠标左键,按住不放
release(on_element=None) 在某个元素位置松开鼠标左键
drag_and_drop(source, target) 拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset) 拖拽到某个坐标然后松开
move_to_element(to_element) 鼠标移动到某个元素
move_by_offset(xoffset, yoffset) 移动鼠标到指定的x,y位置
move_to_element_with_offset(to_element, xoffset, yoffset) 将鼠标移动到距某个元素多少距离的位置
perform() 执行链中的所有动作

动作链示例

通过查看登录按钮的元素,发现其父级类为el-button,确定登录对象

通过查看滑块元素,确定滑块对象geetest_slider_button

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
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

# 选取驱动
wb = webdriver.Chrome()
# 挑选页面
wb.get('https://captcha1.scrape.center/')


def move_silder(tracks):
time.sleep(5)
# 确定登录对象
wb.find_element(by=By.CLASS_NAME, value='el-button').click()

time.sleep(3) # 或者使用隐式代码询问验证滑块是否跳出
# 确定滑块对象
element = wb.find_element(by=By.CLASS_NAME, value='geetest_slider_button')

# 动作链
# 实例化新对象
action_chains = ActionChains(wb)
# 进行模拟操作
action_chains.click_and_hold(element) # 按住不放
action_chains.pause(1) # 在执行下一个动作之前 暂停1秒
action_chains.move_by_offset(tracks, 0) # 滑动tracks的距离
action_chains.pause(1) # 在执行下一个动作之前 暂停1秒
action_chains.move_by_offset(tracks - 50, 0) #滑动tracks-50的距离
action_chains.pause(1) # 在执行下一个动作之前 暂停1秒
action_chains.release().perform() # 执行以上所有动作链


move_silder(150) # 调用函数,tracks = 150

其他

常用方法

方法 说明
Chrome.refresh() 刷新页面
Chrome.close() 关闭当前标签
Chrome.quit() 关闭所有标签
Chrome.page_source 网页源代码
Chrome.cookies 本页保存的cookie
Chrome.maximize_window() 最大化窗口

无界面设置

1
2
3
4
5
6
7
8
#设置chrome  无界面
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
chrome_options=Options()
chrome_options.add_argument("--headless")
wd_headless=webdriver.Chrome(options=chrome_options)
wd_headless.get('http://www.treejs.cn/v3/demo/cn/exedit/drag.html')
print("无界面操纵",wd_headless.page_source)

截图功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
selenium的webdriver对象本身就自带截图功能
"""

from selenium import webdriver


wb = webdriver.Chrome()
wb.maximize_window()
wb.get('https://www.baidu.com')

# wb.save_screenshot('baidu.png') # 建议使用png
# wb.get_screenshot_as_file('baidu01.png')

# print(wb.get_screenshot_as_base64())
# print(wb.get_screenshot_as_png())
  • 标题: Selenium 自动化工具
  • 作者: Yiuhang Chan
  • 创建于 : 2021-02-24 06:12:56
  • 更新于 : 2024-02-28 18:50:27
  • 链接: https://www.yiuhangblog.com/2021/02/24/20210224selenium/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论