1、原理及安装
1.1 原理
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera,Edge等。这个工具的主要功能包括:测试与浏览器的兼容性——测试应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成.Net、Java、Perl等不同语言的测试脚本。—百度百科
1.2 安装
1.2.1 selenium
pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple
1.2.2 Chrome浏览器
1.2.3 Chrome浏览器驱动
-
根据自己的系统选择下载驱动的版本:
1.3 环境配置
- 浏览器驱动下载后将其放在没有中文的路径下(只需留下exe应用程序即可)
- 复制路径将其放入环境变量中
- 若不配置环境变量则每次都需要配置路径
from selenium import webdriver
wd = webdriver.Chrome(service=Service(r'E:\software\anaconda3\envs\crawler\chromedriver.exe'))
- 配置环境变量后则可以省去此步骤
from selenium import webdriver
wd = webdriver.Chrome()
2、元素定位
- 每次使用selenium控制驱动打开浏览器的时候,网页总是自动关闭
from selenium import webdriver
# 创建 WebDriver 对象
wd = webdriver.Chrome()
# 调用WebDriver对象的get方法打开指定网址
wd.get('https://www.byhy.net/_files/stock1.html')
- 可以使用下面的驱动方式使得页面一直显示
from selenium import webdriver
# 不自动关闭浏览器
options = webdriver.ChromeOptions()
options.add_experimental_option("detach", True)
# 将option作为参数添加到Chrome中
wd = webdriver.Chrome(options=options)
wd.get('https://www.baidu.com/')
2.1 通过id定位
# 根据id选择元素,返回的就是该元素对应的WebElement对象
element_id = wd.find_element(By.ID, 'wrapper')
2.2 通过class定位
element_class = wd.find_element(By.CLASS_NAME, "wrapper_new")
print(element_class)
2.3 通过name定位
element_name = wd.find_element(By.NAME, "baidu-site-verification")
print(element_name) # 没有内容只返回对象
print(element_name)
2.4 通过tag_name定位
element_tag_name = wd.find_element(By.TAG_NAME, "meta")
print(element_tag_name) # 没有内容只返回对象
print(element_tag_name)
2.5 通过Xpath语法定位
只能定位到标签位置,并不能直接定位到内容然后返回,只能返回对象再使用.text获取内容
element_Xpath = wd.find_element(By.XPATH, "//div[@class='nav-logo']/a")
print(element_Xpath)
2.6 通过css语法定位
element_CSS = wd.find_element(By.CSS_SELECTOR, "#db-nav-movie")
print(element_CSS)
2.7 通过文本定位
wd.find_element(By.XPATH, "//*[contains(text(),'排行榜')]").click() # 点击并进行跳转操作
我们发现直接使用上面的程序进行获取会报错,这是因为浏览器的加载需要时间,程序没有等待而是直接获取元素,这就导致获取失败
-
解决方式:
-
强制等待:
# 让程序睡眠一秒等待浏览器加载 import time time.sleep(1)
-
显式等待:
# 实际上是依据try-except异常捕获进行判断 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.common.exceptions import TimeoutException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wd = webdriver.Chrome() wd.get("https://movie.douban.com/top250?start=25&filter=") def search(): try: element = WebDriverWait(driver, 10, 0.5).until( EC.presence_of_element_located((By.ID, "wrapper")) ) except TimeoutException: return search()
-
隐式等待:
# 隐式等待,每隔半秒寻找一次元素,最长等10秒 wd.implicitly_wait(10)
-
3、浏览器控制
3.1 修改浏览器窗口大小
3.1.1 修改指定大小
wd.set_window_size(600, 800)
3.1.2 全屏显示
wd.maximize_window()
3.2 浏览器访问
webdriver提供了
back
和forward
两种方式实现页面的前进或后退。下面我们使用豆瓣电影TOP250作为例子进行演示:
1、访问主页
2、进入排行榜
3、进入剧情分类
4、返回排行榜
5、返回主页
6、进入排行榜
import time
from selenium import webdriver
wd = webdriver.Chrome()
# 访问主页链接
wd.get("https://movie.douban.com/top250")
time.sleep(1)
# 进入排行榜
wd.get("https://movie.douban.com/chart") # 在原标签页面打开
# # 新标签中打开
# js = "window.open('https://movie.douban.com/chart')"
# wd.execute_script(js)
time.sleep(1)
# 进入剧情分类
wd.get("https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=")
time.sleep(5)
# 返回排行榜
wd.back()
time.sleep(1)
# 返回主页
wd.back()
time.sleep(1)
# 进入排行榜
wd.forward()
time.sleep(1)
- 刷新页面
wd.refrech()
- 切换页面
# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
wd.switch_to.window(windows[-1])
4、鼠标操作
并不是直接操作我们现在使用的鼠标,而是使用抽象出来的鼠标进行操作
4.1 ActionChains方法
执行动作需要要使用perform()方法,否则动作不执行
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) ——拖拽到某个元素然后松开
key_down(value, element=None) ——按下某个键盘上的键
key_up(value, element=None) ——松开某个键
move_to_element(to_element) ——鼠标移动到某个元素
perform() ——执行链中的所有动作
release(on_element=None) ——在某个元素位置松开鼠标左键
send_keys(*keys_to_send) ——发送某个键到当前焦点的元素
send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素
move_by_offset(x,y) ——移动到某个坐标
4.2 鼠标移动
待执行动作:
1、移动到value值为Write on hover的元素上,显示为Mouse moved;
2、移动到value值为Blank on hover的元素上,显示框清空;
3、最后再移动到value值为Write on hover的元素上。
这种方式对有隐藏属性元素操作十分有用。
action = ActionChains(wd)
time.sleep(2)
action.move_to_element(write_element).perform()
time.sleep(2)
action.move_to_element(blank_element).perform()
time.sleep(2)
action.move_to_element(write_element).perform()
time.sleep(3)
wd.quit()
4.3 鼠标拖拽
待执行动作:
1、将被拖拽元素拖拽到第一个方框,观察变化;
2、将被拖拽元素拖拽到第二个方框,观察变化。
action.drag_and_drop(item, item1).perform()
time.sleep(1)
action.drag_and_drop(item, item3).perform()
time.sleep(5)
4.4 鼠标点击
- 示例网站:http://sahitest.com/demo/clicks.htm
待执行动作:
1、点击单击元素;
2、点击双击元素;
3、点击右击元素;
4、点击选择元素。
# 链式用法
ActionChains(wd).click(click).double_click(click_double).context_click(click_right).\
click(click_select1).click(click_select2).click(click_select3).\
click(click_checkbox).perform()
5、键盘操作
5.1 keys类键盘操作
操作 | 作用说明 |
---|---|
Keys.F1 | F1键 |
Keys.SPACE | 空格 |
Keys.TAB | Tab键 |
Keys.ESCAPE | ESC键 |
Keys.ALT | Alt键 |
Keys.SHIFT | Shift键 |
Keys.ARROW_DOWN | 向下箭头 |
Keys.ARROW_LEFT | 向左箭头 |
Keys.ARROW_RIGHT | 向右箭头 |
Keys.ARROW_UP | 向上箭头 |
5.2 组合操作
操作 | 作用说明 |
---|---|
send_keys(Keys.CONTROL,‘a’) | 全选(Ctrl+A) |
send_keys(Keys.CONTROL,‘c’) | 复制(Ctrl+C) |
send_keys(Keys.CONTROL,‘x’) | 剪切(Ctrl+X) |
send_keys(Keys.CONTROL,‘v’) | 粘贴(Ctrl+V) |
5.3 示例
待执行动作:
1、找到包含排行第一电影名称的元素;
2、将其复制到豆瓣电影的搜索框中;
3、使用鼠标操作进行点击。
# 定位到第一个电影标题
title = wd.find_element(By.XPATH, "//div[@class='hd']/a/span[1]")
# 获取元素的文本
text_to_copy = title.text
# # 使用JavaScript将文本复制到剪贴板
wd.execute_script("window.getSelection().selectAllChildren(arguments[0]);window.getSelection().rangeCount > 0 && window.getSelection().getRangeAt(0).deleteContents();", title)
# wd.execute_script("document.execCommand('copy')")
action = ActionChains(wd)
action.send_keys(Keys.CONTROL, "c")
# 定位到搜索框
input_ = wd.find_element(By.XPATH, "//div[@class='inp']/input")
# 粘贴复制的文本到搜索框
input_.send_keys(text_to_copy)
# 等待搜索按钮加载完成
search_button = WebDriverWait(wd, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[@value='搜索']")))
# 点击搜索按钮
search_button.click()
6、元素等待
6.1 强制等待
# 让程序睡眠一秒等待浏览器加载
import time
time.sleep(1)
6.2 显式等待
# 使用EC类中的方法进行判断
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wd = webdriver.Chrome()
wd.get("https://movie.douban.com/top250?start=25&filter=")
def search():
try:
element = WebDriverWait(driver, 10, 0.5).until(
EC.presence_of_element_located((By.ID, "wrapper")) # 判断ID为wrapper元素是否可见
)
except TimeoutException:
return search()
方法 | 描述 |
---|---|
title_is(‘百度一下’) | 判断当前页面的 title 是否等于预期 |
title_contains(‘百度’) | 判断当前页面的 title 是否包含预期字符串 |
presence_of_element_located(locator) | 判断元素是否被加到了 dom 树里,并不代表该元素一定可见 |
visibility_of_element_located(locator) | 判断元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0 |
text_to_be_present_in_element(locator , ‘百度’) | 判断元素中的 text 是否包含了预期的字符串 |
ext_to_be_present_in_element_value(locator , ‘某值’) | 判断元素中的 value 属性是否包含了预期的字符串 |
frame_to_be_available_and_switch_to_it(locator) | 判断该 frame 是否可以 switch 进去,True 则 switch 进去,反之 False |
invisibility_of_element_located(locator) | 判断元素中是否不存在于 dom 树或不可见 |
element_to_be_clickable(locator) | 判断元素中是否可见并且是可点击的 |
staleness_of(element) | 等待元素从 dom 树中移除 |
element_to_be_selected(element) | 判断元素是否被选中,一般用在下拉列表 |
element_selection_state_to_be(element, True) | 判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False |
element_located_selection_state_to_be(locator, True) | 跟上一个方法作用相同,但传入参数为 locator |
alert_is_present() | 判断页面上是否存在 alert |
6.3 隐式等待
# 隐式等待,每隔半秒寻找一次元素,最长等10秒,超过时间后返回异常
wd.implicitly_wait(10)
7、高级操作
7.1 无界面启动
优势:
1、速度更快:Headless模式下的测试速度通常比普通模式更快。这对于大规模测试或需要频繁执行的 测试场景非常有利。
2、更加稳定Headless模式下的测试不会弹出可见的浏览器窗口,可以在后台静默运行,不影响用户体 验。同时,由于无需考虑浏览器窗口的操作,测试更稳定,容易集成到持续集成(CI)系统中。
from selenium import webdriver
option = webdriver.ChromeOptions()
# 加载浏览器的静默模式
option.add_argument("--headless")
driver = webdriver.Chrome(options=option)
driver.get("https://www.baidu.com")
print(driver.page_source)
driver.quit()
7.2 页面切换
点击跳转链接打开新页面时,selenium依然在之前的页面上进行操作,若想在新页面操作,需要更新操作页面。
# 返回的值是一个列表,按照时间顺序进行排序
wd.window_handles[i]
from selenium.webdriver.common.by import By
from selenium import webdriver
import time
handles = []
wd = webdriver.Chrome()
wd.get("https://movie.douban.com/top250?start=")
wd.implicitly_wait(5)
handles.append(wd.current_window_handle)
wd.find_element(By.XPATH, "//div[@class='global-nav-items']/ul/li/a").click()
time.sleep(1)
# 切换到最新打开的窗口
# wd.switch_to.window(wd.window_handles[-1])
handles.append(wd.current_window_handle)
print(handles)
>>>['74AB56A47773D439438ECAB53147B24B', '74AB56A47773D439438ECAB53147B24B']
from selenium.webdriver.common.by import By
from selenium import webdriver
import time
handles = []
wd = webdriver.Chrome()
wd.get("https://movie.douban.com/top250?start=")
wd.implicitly_wait(5)
handles.append(wd.current_window_handle)
wd.find_element(By.XPATH, "//div[@class='global-nav-items']/ul/li/a").click()
time.sleep(1)
# 切换到最新打开的窗口
wd.switch_to.window(wd.window_handles[-1])
handles.append(wd.current_window_handle)
print(handles)
>>>['5FEA7C45627785F02C6B7D56409D0E39', '8EC96C8F3F2978F524D49AE2F54F55A1']
- 通过观察句柄的异同,我们可以判断,实现了页面的更新。
7.3 表单切换
在页面中有时可能遇到
frame/iframe
表单嵌套的操作,无法直接获取被嵌套元素,这时我们就需要进行表单切换操作。
# 以QQ切换为密码登录为例
from selenium.webdriver.common.by import By
from selenium import webdriver
import time
handles = []
wd = webdriver.Chrome()
wd.get("https://qzone.qq.com/")
wd.implicitly_wait(5)
login_frame = wd.find_element(By.ID, "login_frame")
# 切换到iframe
wd.switch_to.frame(login_frame)
wd.find_element(By.ID, "switcher_plogin").click()
username = wd.find_element(By.XPATH, "//input[@id='u']")
password = wd.find_element(By.XPATH, "//input[@id='p']")
username.send_keys("123")
password.send_keys("123")
wd.find_element(By.XPATH, "//a[@class='login_button']").click()
time.sleep(1)
7.4 selenium模拟Javascript
7.4.1 弹窗处理
获取弹窗内容方式。
方法 | 描述 |
---|---|
text | 获取弹窗中的文字 |
accept | 接受(确认)弹窗内容 |
dismiss | 解除(取消)弹窗 |
send_keys | 发送文本至警告框 |
7.4.2 模拟动作
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.jd.com/')
temp_height = 0
while True:
# 滑动下拉框
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(1)
check_height = driver.execute_script(
"return document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;")
if check_height == 11922:
break
7.5 cookies操作
7.5.1 调用方法
方法 | 描述 |
---|---|
get_cookies | 获得所有cookies信息 |
get_cookies(name) | 返回指定name名称的cookies信息 |
add_cookies(cookie_dict) | 添加cookie,必须有name值和value值 |
delete_cookie(name) | 删除特定的cookie信息 |
delete_all_cookies() | 删除所有的cookie信息 |
7.5.2 cookies属性值
属性 | 含义 |
---|---|
name | 必选参数,规定cookies的名称 |
value | 必选参数,规定cookies的值 |
expire | 可选参数,规定cookies的有效期 |
domain | 可选参数,规定cookies的域名 |
path | 可选参数,规定cookies服务器的路径 |
secure | 可选参数,规定是否通过安全的https连接来传输cookies |
httpOnly | 可选参数,防止XSS攻击(跨站脚本攻击) |
7.6 其他操作
7.6.1 常见操作
# 截图
get_screenshot_as_file()
# 获取当前页面url
driver.current_url
# 获取当前html源码
driver.page_source
# 获取当前页面标题
driver.title
# 获取浏览器名称
driver.name
# 对页面进行截图,返回二进制数据
driver.get_screenshot_as_png()
# 设置浏览器尺寸
driver.get_window_size()
# 获取浏览器尺寸,位置
driver.get_window_rect()
# 获取浏览器位置(左上角)
driver.get_window_position()
# 设置浏览器尺寸
driver.set_window_size(width=640, height=480)
# 设置浏览器位置(左上角)
driver.set_window_position(x=500, y=600)
# 设置浏览器的尺寸,位置
driver.set_window_rect(x=200, y=400, width=1000, height=600)
7.6.2 隐藏selenium指纹特征
import time
from selenium.webdriver import Chrome
option = webdriver.ChromeOptions()
option.add_argument("--headless")
# 无头浏览器需要添加user-agent来隐藏特征
option.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36')
driver = Chrome(options=option)
driver.implicitly_wait(5)
with open('stealth.min.js') as f:
js = f.read()
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": js
})
driver.get('https://bot.sannysoft.com/')
driver.save_screenshot('hidden_features.png')