Selenium简介
Selenium是浏览器的自动化测试工具,与浏览器进行交互,实现对web应用的自动化测试,Selenium包括Selenium IDE, Selenium Webdriver和Selenium Grid三个工具。
- Selenium IDE (Integrated Development Environment)是一个浏览器插件,提供脚本录制、脚本生成和回放功能,初次使用selenium的新手可以用它来做一些简单的测试,
- Selenium Webdriver是一个浏览器自动化框架,接受脚本命令并发送到浏览器(通过浏览器驱动来实现),支持多种语言(包括Java, Ruby, Python, PHP, JavaScript, C#等)和多种浏览器,并且支持windows,Linux,macOS等操作系统。
- Selenium Grid实现在多个机器上并行运行selenium,也就是同时在多个机器上执行测试,并且可以是不同的浏览器和操作系统(跨平台)。
这里主要介绍Webdriver,主要原因是我使用Selenium的主要目的是做数据的抓取。
Selenium Webdriver简介
Selenium Webdriver API实现脚本语言与浏览器之间的通信,Selenium Webdriver架构包括四个基本组件:
- Selenium Language Bindings/Selenium Client Library:Selenium语言绑定/客户端库
- JSON Wire Protocol:JSON有线协议
- Browser Driver:浏览器驱动
- Browser:浏览器

Selenium支持多种语言,包括Ruby、Java、Python、C#、JavaScript、GO、Haskell、JavaScript、Perl、PHP、R和Dart。执行测试用例时,selenium代码将被转换为JSON格式,发送给浏览器驱动。
开始使用Selenium前,需要了解一下一个自动化测试过程涉及的几个主要组成部分。它们是WebDriver(Selenium提供的针对各个语言的浏览器操作库)、Driver(浏览器驱动)和Browser(浏览器)。
这三个部分的交互过程如下图所示:

可以看到,WebDriver通过Driver来与Browser进行双向通信。即WebDriver通过Driver传递指令给Browser;然后WebDriver再由Driver接收Browser的响应信息。
需要说明的是:该图展示的情形中,WebDriver与Browser(及Driver)位于同一主机。但使用Selenium Grid后,WebDriver可与Browser(及Driver)位于不同的主机。
Python+Selenium爬虫
环境搭建
安装Selenium
pip install selenium
下载驱动
由于Selenium需要对浏览器进行操作,因此除了Selenium,我们还需要下载浏览器驱动。需要说明的是,部分Selenium教程中使用的是PhantomJS,然而由于PhantomJS已于2018年后停止维护,因此不推荐使用。
Selenium支持大多数主流浏览器,对应的驱动可以在如下地址中下载:
- Chrome:Chrome for Testing availability
- Edge:Microsoft Edge WebDriver | Microsoft Edge Developer
- FireFox:Releases mozilla/geckodriver (github.com)
浏览器的驱动与浏览器版本相关,因此需要下载对应版本的驱动。驱动下载后,需要将其加入环境变量中。
手动创建一个存放浏览器驱动的目录,如:E:\BrowserDriver,将下载的浏览器驱动文件解压后,如chromedriver.exe、geckodriver.exe丢到该目录下,接着设置环境变量。将“E:\BrowserDriver”目录添加到Path的值中。
测试是否安装成功:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.baidu.com/")
Selenium快速体验
以下代码是使用Seleniu抓取豆瓣TOP250电影相关信息的示例:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import csv
#创建Chrome浏览器对象
browser = webdriver.Chrome()
#打开豆瓣电影TOP250页面
browser.get("https://movie.douban.com/top250")
#创建一个列表来保存所有电影数据
movies = []
#循环遍历所有页
while True:
#获取当前页面的电影信息
movie_items = browser.find_elements(By.CSS_SELECTOR, ".item")
for movie in movie_items:
#获取电影信息
title = movie.find_element(By.CSS_SELECTOR, ".title").text
rating = movie.find_element(By.CSS_SELECTOR, ".rating_num").text
#将电影信息添加到列表中
movies.append([title, rating])
#查找是否有下一页按钮
next_page = browser.find_elements(By.CSS_SELECTOR, ".next a")
if next_page:
#如果有,点击下一页按钮
next_page[0].click()
#等待页面加载完成
time.sleep(2)
else:
#如果没有下一页按钮,退出循环
break
#关闭浏览器窗口
browser.quit()
#创建一个CSV文件并写入电影数据
with open("movies.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Title", "Rating"])
writer.writerows(movies)
Selenium使用说明
WebDriver基础使用
WebDriver的基础使用,我们主要利用官网提供的WebForm示例页面,该页面包含文本输入框、下拉框、文件上传框、日期选择框等,来讲解如何使用 WebDriver 创建浏览器对象、打开页面、定位元素、输入内容和点击按钮等操作。
from unittest import TestCase
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
class TestSeleniumForm(TestCase):
def setUp(self) -> None:
# 无痕模式的 Chrome
options = webdriver.ChromeOptions()
options.add_argument('--incognito')
self.browser = webdriver.Chrome(options=options)
self.addCleanup(self.browser.quit)
def test_web_form(self) -> None:
# 打开表单页面
self.browser.get('https://www.selenium.dev/selenium/web/web-form.html')
self.assertEqual(self.browser.title, 'Web form')
# Text 输入
text_input = self.browser.find_element(By.ID, 'my-text-id')
text_input.send_keys('Selenium')
# Password 输入
password = self.browser.find_element(By.NAME, 'my-password')
password.send_keys('Selenium')
# Dropdown 选择 Two
dropdown = Select(self.browser.find_element(By.NAME, 'my-select'))
dropdown.select_by_value('2')
# 选择文件
file_input = self.browser.find_element(By.CSS_SELECTOR, 'input[name="my-file"]')
file_input.send_keys('D:\\CodeHub\\SeleniumSpider\\movies.csv')
# 日期选择
date_input = self.browser.find_element(By.XPATH, '//input[@name="my-date"]')
date_input.send_keys('04/21/2023')
# 点击 Submit 按钮
submit_button = self.browser.find_element(By.XPATH, '//button[@type="submit"]')
submit_button.click()
# 等待进入已提交页面
WebDriverWait(self.browser, 10).until(EC.title_is('Web form - target page'))
# 断言
message = self.browser.find_element(By.ID, 'message').text
self.assertEqual(message, 'Received!')
可以看到,我们使用 WebDriver 实现了对页面表单的自动化输入与提交。
下面总结一下,我们所使用 WebDriver 的几个关键方法。
browser 实例的创建
可以指定参数来创建一个特定浏览器实例。
self.browser = webdriver.Chrome(options=options)
页面打开
可以使用 get 方法来打开一个 URL。
self.browser.get('https://www.selenium.dev/selenium/web/web-form.html')
元素定位
可以使用 ID、NAME、CSS 选择器、XPATH 等多种方式定位页面元素。
self.browser.find_element(By.ID, 'my-text-id') self.browser.find_element(By.NAME, 'my-password') self.browser.find_element(By.CSS_SELECTOR, 'input[name="my-file"]') self.browser.find_element(By.XPATH, '//input[@name="my-date"]')
对元素进行操作
可以对元素进行输入或点击操作。
text_input.send_keys('Selenium')
submit_button.click()
等待页面元素出现
可以使用 WebDriverWait 来等待页面的某个元素出现。
WebDriverWait(self.browser, 10).until(EC.title_is('Web form - target page'))
浏览器对象的销毁
最后需要调用 quit 方法来关闭浏览器窗口,释放资源。
self.browser.quit()
页面加载策略
Selenium WebDriver 的浏览器选项有三种页面加载策略可供选择,它们是:normal、eager 和 none。了解它们代表什么之前,先介绍一下加载及渲染一个 Web 页面大概有哪些阶段。
按事件分的话,一个网页的生命周期主要有 DOMContentLoaded、load、beforeunload 和 unload 这几个阶段。
- DOMContentLoaded。HTML 文档已加载完成,DOM 树已构建完成,但依赖的脚本、图片、样式表、iFrame 等外部资源可能还没有加载完成。
- Load。不仅 HTML 文档已加载完成,依赖的脚本、图片、样式表、iFrame 等外部资源均已加载完成。
- Beforeunload。用户离开前的前置事件。
- Unload。用户已经离开。
按 document.readyState 分的话,只有 loading、interactive 和 complete 这三个阶段。
- loading。HTML 文档仍在加载。
- interactive。HTML 文档已加载并解析完成,但依赖的脚本、图片、样式表、iFrame 等外部资源可能还没有加载完成。
- complete。HTML 文档以及依赖的脚本、图片、样式表、iFrame 等外部资源均已加载完成。
下图将这两种方式组合到一起来看一下一个网页的生命周期:
可以看到,事件里的 DOMContentLoaded 对应 document.readyState 里的 interactive;事件里的 load 对应 document.readyState 里的 complete。而 Selenium WebDriver 支持的三种加载策略与事件和 document.readyState 的对应关系如下表所示:
| Selenium 页面加载策略 | 对应的事件 | 对应的 document.readyState |
| normal(默认值) | load | complete |
| eager | DOMContentLoaded | interactive |
| none | 无 | Any(任何状态都可以) |
可以看到,当访问一个 URL 时,Selenium WebDriver 的默认策略是等待整个页面全部加载完成(除了使用 JavaScript 在 load 事件后再动态添加内容)。在编写自动化测试用例时,如果测试逻辑不依赖外部资源的加载,即可以将页面加载策略从默认选项 normal 改为 eager 或 none 来加速测试过程。
更改 Selenium WebDriver 页面加载策略的示例 Python 代码如下:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.page_load_strategy = 'eager' # 'none', 'normal'
driver = webdriver.Chrome(options=options)
driver.get('https://www.baidu.com')
driver.quit()
等待策略
通俗点讲,WebDriver 是一个告诉浏览器做什么的库。因 Web 页面具有一定的异步特性,且 WebDriver 不会实时跟踪 DOM 的状态;所以,有些情况下,定位元素时,可能会出现「no such element」错误。下面看一段代码:
from selenium import webdriver
from selenium.webdriver import Keys
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/')
text_input = driver.find_element(By.ID, 'kw')
text_input.send_keys('Selenium' + Keys.RETURN)
# 会抛出 NoSuchElementException
first_result_title = driver.find_element(By.XPATH, '//div[@id="content_left"]/div[1]/h3').text
print(first_result_title)
driver.quit()
这段代码打开了百度首页,然后键入关键字 Selenium 后回车进行搜索,接着即找第一个结果的标题进行打印。运行该代码时,会抛出 NoSuchElementException,原因是定位元素的时候,搜索结果页面还没有完全打开,因此未找到对应的元素。
遇到这样的问题怎么办呢?可以通过 Selenium WebDriver 提供的显式等待或隐式等待功能来解决。
显式等待
显式等待,即程序暂停执行直至传递的条件满足。显式等待非常适合被用来做 WebDriver 与 DOM 的状态同步。上面抛出「no such element」错误的代码可使用显式等待的方式改造为:
from selenium import webdriver
from selenium.webdriver import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/')
text_input = driver.find_element(By.ID, 'kw')
text_input.send_keys('Selenium' + Keys.RETURN)
# 等待搜索结果展示
WebDriverWait(driver, timeout=True, poll_frequency=10).until(EC.presence_of_element_located((By.ID, 'content_left')))
# 不会抛出异常
first_result_title = driver.find_element(By.XPATH, '//div[@id="content_left"]/div[1]/h3').text
print(first_result_title)
driver.quit()
可以看到,我们新建了一个 WebDriverWait 对象(指定了超时时间),并使用 expected_conditions.presence_of_element_located() 方法为其设置了跳出条件。除此方法外,expected_conditions 包下常用的方法还有 expected_conditions.url_contains() 与 expected_conditions.title_is() 等。
由于显示等待是针对条件的判断,在日常使用中,我们的很多操作都需要同步 DOM,因此 Selenium 针对这些常用的操作,预定义了其对应的预期条件(Expected conditions),对 Python 接口来说,具体包含如下内容:
| 预期条件 | 释义 |
| alert_is_present | 预期出现提示框 |
| element_located_selection_state_to_be | 预期节点元素选择状态 |
| element_selection_state_to_be | 预期节点元素选择状态 |
| element_located_to_be_selected | 预期节点元素为选择状态 |
| element_to_be_clickable | 预期元素可点击(可见+使能) |
| element_to_be_selected | 预期元素处于选中状态 |
| frame_to_be_available_and_switch_to_it | 预期 frame 可用,同时切换到该 frame 中 |
| visibility_of | 预期节点可见(节点必须已经加载到当前 DOM 上) |
| visibility_of_element_located | 预期节点可见 |
| visibility_of_all_elements_located | 预期指定的所有节点可见 |
| visibility_of_any_elements_located | |
| invisibility_of_element | 预期节点元素不可见或不存在 |
| invisibility_of_element_located | 预期节点元素不可见或不存在 |
| new_window_is_opened | 预期新开窗口,同时窗口数量增加 |
| number_of_windows_to_be | 预期窗口数量 |
| presence_of_all_elements_located | 预期所有节点加载完成 |
| presence_of_element_located | 预期节点元素加载完成(无需为可见状态) |
| staleness_of | 等待直到预期元素脱离DOM |
| text_to_be_present_in_element | 预期节点文本 |
| text_to_be_present_in_element_value | 预期节点元素value值 |
| title_contains | 预期标题包含相关子串(区分大小写) |
| title_is | 预期标题(完全匹配) |
| url_changes | 预期URL更改 |
| url_contains | 预期当前URL包含相关子串(区分大小写) |
| url_matches | 预期当前URL匹配指定模式 |
| url_to_be | 预期当前URL内容(完全匹配) |
expected_conditions()类:
- element_located_selection_state_to_be():判断一个元素的状态是否是给定的选择状态
- element_selection_state_to_be():判断给定的元素是否被选中
- element_located_to_be_selected():期望某个元素处于被选中状态
- element_to_be_selected():期望某个元素处于选中状态
- element_to_be_clickable():判断元素是否可见并且能被单击,条件满足返回页面元素对象,否则返回Flase
- frame_to_be_available_and_switch_to_it():判断frame是否可用
- invisibility_of_element_located():希望某个元素不可见或者不存在DOM中,满足条件返回True,否则返回定位到的元素对象
- visibility_of_element_located():希望某个元素出现在DOM中并且可见,满足条件返回该元素的页面元素对象
- visibility_of():希望某个元素出现在页面的DOM中,并且可见,满足条件返回该元素的页面元素对象
- visibility_of_any_elements_located():希望某个元素出现在DOM中并且可见
- presence_of_all_elements_located():判断页面至少有一个如果元素出现,如果满足条件,返回所有满足定位表达式的压面元素
- presence_of_element_located(locator):判断某个元素是否存在DOM中,不一定可见,存在返回该元素对象
- staleness_of(webelement):判断一个元素是否仍在DOM中,如果在规定时间内已经移除返回True,否则返回Flase
- text_to_be_present_in_element():判断文本内容test是否出现在某个元素中,判断的是元素的text
- text_to_be_present_in_element_value():判断text是否出现在元素的value属性值中
- title_contains():判断页面title标签的内容包含partial_title,只需要部分匹配即可,包含返回True,不包含返回Flase
- title_is():判断页面title内容是与传入的title_text内容完全匹配,匹配返回True,否则返回Flase
隐式等待
隐式等待是告诉WebDriver在查找元素时,若不存在,即轮询DOM一段时间。其一般在新建WebDriver时设置,对整个会话有效。上面抛出「nosuchelement」错误的代码可使用隐式等待的方式改造为:
from selenium import webdriver
from selenium.webdriver import Keys
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
# 设置隐式等待时间
driver.implicitly_wait(10)
driver.get('https://www.baidu.com/')
text_input = driver.find_element(By.ID, 'kw')
text_input.send_keys('Selenium' + Keys.RETURN)
# 不会抛出异常
first_result_title = driver.find_element(By.XPATH, '//div[@id="content_left"]/div[1]/h3').text
print(first_result_title)
driver.quit()
implicitly_wait(10)如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间结束。
真实的测试场景,一般只建议使用显式等待。
强制等待
强制让浏览器等待,不管当前操作是否完成。
import time time.sleep(3)
获取文本值或属性
不管是在做功能测试还是自动化测试,经常需要获取定位元素的文本值或属性。Selenium提供了page_source, title, current_url, text和get_attribute方法来实现这个目的。
示例代码:
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox(executable_path="F:\GeckoDriver\geckodriver")
driver.get("https://www.baidu.com")
print('Before search================')
# 打印当前页面源码
source = driver.page_source
print(source)
# 打印当前页面title
title = driver.title
print(title)
# 打印当前页面URL
now_url = driver.current_url
print(now_url)
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
sleep(1)
print('After search================')
# 再次打印当前页面title
title = driver.title
print(title)
# 打印当前页面URL
now_url = driver.current_url
print(now_url)
# 获取结果数目
user = driver.find_element_by_class_name('nums').text
keywords = driver.find_element_by_id("kw").get_attribute("value")
print(user)
print(keywords)
# 关闭所有窗口
driver.quit()
元素定位与操作
定位与操作DOM中的元素是使用Selenium编写自动化测试用例的主要工作。
元素定位
Selenium WebDriver提供8种基本的元素定位方法。
| 定位方法 | 描述 |
| id | 查找id属性与搜索值匹配的元素 |
| name | 查找name属性与搜索值匹配的元素 |
| class name | 查找class名包含搜索值的元素 |
| css selector | 查找与CSS选择器匹配的元素 |
| link text | 查找其可见文本与搜索值匹配的锚元素 |
| partial link text | 查找其可见文本包含搜索值的锚元素。如有多个,则仅选择第一个元素。 |
| tag name | 查找tag名与搜索值匹配的元素 |
| xpath | 查找与XPath表达式匹配的元素 |
如下为百度搜索框input标签的HTML代码:
<input id="kw" name="wd" class="s_ipt" maxlength="255" autocomplete="off"/>
可使用如下几种方式来定位到该input元素:
driver.find_element(By.ID,'kw') driver.find_element(By.CLASS_NAME,'s_ipt') driver.find_element(By.NAME,'wd') driver.find_element(By.XPATH,'//input[@name="wd"]')
此外,Selenium在版本4引入了相对定位器,即可使用空间相对位置来定位一个元素,其可在传统定位器无法描述时使用。
这两个输入框的HTML代码如下:
<input type="text" name="my-text" id="my-text-id"/> <input type="password" name="my-password" autocomplete="off"/>
若Password输入框采用传统方法不好定位,则可使用相对定位器来定位:
password_locator = locate_with(By.TAG_NAME,'input').below({By.ID:'my-text-id'})
driver.find_element(password_locator)
元素操作
Selenium提供4个基本的元素操作命令。它们是:Click、SendKeys、Clear、Select。
下面使用一个例子来演示如何使用这几个命令。
如下为百度关键字输入框和「百度一下」搜索按钮的HTML代码:
<input id="kw" name="wd" class="s_ipt" maxlength="255" autocomplete="off"/> ... <input type="submit" id="su" value="百度一下" class="bgs_btn"/>
可使用如下命令进行关键字清除、键入关键字和点击搜索按钮操作:
input_text = driver.find_element(By.ID,'kw')
input_text.clear()
input_text.send_keys('Selenium')
driver.find_element(By.ID,'su').click()
关于Select命令的使用,同样使用Selenium官网的「Web表单示例页面」作示例。
该页面上的Dropdown(select)是一个单选框,其HTML代码如下:
<select class="form-select" name="my-select"> <option selected="">Open this select menu</option> <option value="1">One</option> <option value="2">Two</option> <option value="3">Three</option> </select>
使用Select对象选择下拉选项的Python代码如下:
dropdown = Select(driver.find_element(By.NAME,'my-select'))
dropdown.select_by_value('2')
浏览器操作
导航操作进行浏览器导航操作的Python代码如下:
# 打开网址
driver.get('https://selenium.dev')
# 点击向后按钮
driver.back()
# 点击向前按钮
driver.forward()
# 点击刷新按钮
driver.refresh()
原生弹窗操作
可使用 Selenium WebDriver 来与三种原生的消息弹窗(Alert、Confirm 和 Prompt)交互。
下面,先看一下用于演示这三种弹窗的 HTML 代码:
<!DOCTYPE html>
<html>
<head>
<title>Alerts, Prompts and Confirmations test</title>
<script>
function exampleAlert(){
alert("This is an example alert");
}
function exampleConfirm(){
let confirmed = confirm("Do you want to confirm?");
document.getElementById("confirmed").innerText = confirmed;
}
function examplePrompt(){
let favoriteSport = prompt(
"What is your favorite sport?",
"Basketball"
);
document.getElementById("favorite-sport").innerText = favoriteSport;
}
</script>
</head>
<body>
<table border="1">
<tr>
<td><a onclick="exampleAlert()">Click to see an example alert</a></td>
<td></td>
</tr>
<tr>
<td>
<a onclick="exampleConfirm()">Click to see an example confirm</a>
</td>
<td><p id="confirmed"></p></td>
</tr>
<tr>
<td><a onclick="examplePrompt()">Click to see an example prompt</a></td>
<td><p id="favorite-sport"></p></td>
</tr>
</table>
</body>
</html>
接着,看一下测试如上 HTML 页面三种弹窗的 Python 代码:
from unittest import TestCase
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TestAlerts(TestCase):
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.addCleanup(self.driver.quit)
def test_alert(self) -> None:
# 打开 Alerts 示例页面
self.driver.get('file:///Users/larry/Desktop/alerts-test.html')
# 点击超链接 "Click to see an example alert"
self.driver.find_element(By.LINK_TEXT, 'Click to see an example alert').click()
# 等待窗口弹出,获取 Alert 信息,点击 OK
alert = WebDriverWait(self.driver, 10).until(EC.alert_is_present())
alert_message = alert.text
alert.accept()
# 断言
self.assertEqual(alert_message, 'This is an example alert')
def test_confirm(self) -> None:
# 打开 Alerts 示例页面
self.driver.get('file:///Users/larry/Desktop/alerts-test.html')
# 点击超链接 "Click to see an example confirm"
self.driver.find_element(By.LINK_TEXT, 'Click to see an example confirm').click()
# 等待窗口弹出,点击 OK
alert = WebDriverWait(self.driver, 10).until(EC.alert_is_present())
alert.accept()
# 获取 `#confirmed` 文本
confirmed = self.driver.find_element(By.ID, 'confirmed').text
# 断言
self.assertEqual(confirmed, 'true')
def test_prompt(self) -> None:
# 打开 Alerts 示例页面
self.driver.get('file:///Users/larry/Desktop/alerts-test.html')
# 点击超链接 "Click to see an example prompt"
self.driver.find_element(By.LINK_TEXT, 'Click to see an example prompt').click()
# 等待窗口弹出,输入信息,点击 OK
alert = WebDriverWait(self.driver, 10).until(EC.alert_is_present())
alert.send_keys('Football')
alert.accept()
# 获取 `#favorite-sport` 文本
favorite_sport = self.driver.find_element(By.ID, 'favorite-sport').text
# 断言
self.assertEqual(favorite_sport, 'Football')
窗口与选项卡操作
可使用 Selenium WebDriver 来打开、关闭和切换窗口或选项卡。
操作窗口或选项卡的示例 Python 代码如下:
# 获取所有的窗口或选项卡句柄
driver.window_handles
# 获取当前窗口或选项卡的句柄
driver.current_window_handle
# 切换窗口或选项卡
driver.switch_to.window(handle)
# 新建窗口
driver.switch_to.new_window('window')
# 新建选项卡
driver.switch_to.new_window('tab')
# 关闭当前窗口或选项卡
driver.close()
Cookie 操作
有时候我们需要验证浏览器中 cookie 是否正确,因为基于真实 cookie 的测试是无法通过白盒和集成测试进行的。WebDriver 提供了操作 Cookie 的相关方法,可以读取、添加和删除 cookie 信息。
查询、添加和删除 Cookie 的示例 Python 代码如下:
from selenium import webdriver
driver = webdriver.Chrome()
# 打开URL
driver.get('https://www.baidu.com')
# 将Cookie添加到当前浏览器
driver.add_cookie({'name': 'foo', 'value': 'bar'})
# 获取所有的Cookie
print(driver.get_cookies())
# 获取名为foo的Cookie信息
print(driver.get_cookie('foo'))
# 删除名为foo的Cookie信息
driver.delete_cookie('foo')
# 删除所有的Cookie
driver.delete_all_cookies()
driver.quit()
复用Cookie
如果我们使用Selenium模拟登录操作,当然是可行的,但是有些登录操作比较复杂,并且现在网站有相当多的登录验证都得人工进行操作才可以(比如图片识别…),用Selenium模拟登录通常来说是一个费力不讨好的事情,因为无论多复杂的登录操作,目的就是为了获取得到相应的Cookie,而Selenium是有提供Cookie操作的API哦,那其实我们完全可以手动进行登录,然后直接从浏览器开发者工具抓取到需要的Cookie字符串,设置到Selenium中即可。具体代码如下所示:
# 切割字符串,获取每条Cookie键值
def str2Cookie(cookieStr):
def getCookieInfo(cookie):
return cookie.split('=', maxsplit=1)
for cookie in cookieStr.split(';'):
name, value = getCookieInfo(cookie.strip())
yield {'name': name, 'value': value}
def imitateLogin(driver):
# 手动抓取的Cookie字符串
cookieStr = r''
# 必须先访问页面,然后才能操作Cookie
driver.get('https://www.jianshu.com/')
# 清空Cookie
driver.delete_all_cookies()
# 解析cookieStr,并添加到selenium当前会话的的Cookie中
for cookie in str2Cookie(cookieStr):
driver.add_cookie(cookie)
# 刷新当前页面,使Cookie生效
driver.refresh()
if __name__ == '__main__':
driver = webdriver.Chrome()
imitateLogin(driver)
巧用Cookie
持久化Cookie:上面内容我们是直接手动获取Cookie,这种做法可能存在Cookie抓取不完全,导致某些页面无法访问。其实更好地做法是对Cookie进行持久化,我们只需使用Selenium模拟一个登录,然后持久化此时的Cookie,下次再次登录时,直接加载这些Cookie,无需进行真实登录操作。具体持久化方法如下所示:
# 持久化Cookie
def saveCookies(cookies, filename='cookies.json'):
with open(filename, mode='w', encoding='utf-8') as file:
import json
file.write(json.dumps(cookies))
# 加载Cookie
def loadCookies(filename='cookies.json'):
cookies = None
try:
with open(filename, mode='r', encoding='utf-8') as file:
import json
cookies = json.loads(file.read())
except FileNotFoundError:
cookies = None
except PermissionError:
cookies = None
return cookies
if __name__ == '__main__':
driver = webdriver.Chrome()
# 需要先访问下网址
driver.get('https://www.jianshu.com/')
# 先获取持久化的Cookie
cookies = loadCookies()
# Cookie存在
if cookies is not None:
# 清空重置Cookie
driver.delete_all_cookies()
# 添加Cookie
for cookie in cookies:
driver.add_cookie(cookie)
# 刷新一下
driver.refresh()
# Cookie不存在,则进行真实登录
else:
# 此处进行真正的登录操作
# 登录成功后,持久化此时的Cookie
saveCookies(driver.get_cookies())
# 现在就是已登录状态
鼠标操作
ActionChains类常用于模拟鼠标的行为,比如单击,双击,拖拽等行为:
from selenium.webdriver.common.action_chains import ActionChains
常用方法:
- click() 鼠标单击
- click_and_hold() 鼠标单击长按
- context_click() 右击
- double_click() 双击
- drag_and_drop_by_offset(source, xoffset, yoffset)
- source: The element to mouse down.
- xoffset: X offset to move to.
- yoffset: Y offset to move to.
- key_down() 按下某个键
- key_up() 放开某键
键盘操作
from selenium.webdriver.common.keys import Keys
常用按键(其他可以查看keys.py文件)
组合
- 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)
非组合
- 回车键: Keys.ENTER
- 删除键: Keys.BACK_SPACE
- 空格键: Keys.SPACE
- 制表键: Keys.TAB
- 回退键: Keys.ESCAPE
- 刷新键: Keys.F5
执行JavaScript代码
Selenium虽然提供了很多API供我们操作浏览器,但是还是无法覆盖所有内容。因此,Selenium提供了一项本人认为是杀手级功能的特性:执行JavaScript脚本
这项功能其实赋予了我们几乎可以完全操控浏览器的功能,Selenium WebDriver提供的接口为:WebDriver.execute_script(script, *args)
示例:加载网页后,让其垂直滚动到最底部。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://baidu.com')
# window.scrollTo(x,y)
driver.execute_script(f'window.scrollTo(0, {driver.get_window_size()["height"]});')
Chrome options参数
Chrome options是配置chrome启动时属性的类。设置 chrome 二进制文件位置(binary_location)
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() options.binary_location = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" path = "C:\\Program Files(x86)\\chromedriver_win32\\chromedriver.exe" driver = webdriver.Chrome(options=options, executable_path=path)
添加启动参数(add_argument)
# 设置 ua
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument('user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"')
driver = webdriver.Chrome(chrome_options=options)
# 设置 ip 代理
from selenium import webdriver
options = webdriver.ChromeOptions()
chromeOptions = webdriver.ChromeOptions()
chromeOptions.add_argument('--proxy-server=http://ip:port') # 设置 ip 还有端口
driver = webdriver.Chrome(chrome_options=chromeOptions)
# 其他常用
chrome_options.add_argument('--user-agent=""') # 设置请求头的 User-Agent
chrome_options.add_argument('--window-size=1280x1024') # 设置浏览器分辨率(窗口大小)
chrome_options.add_argument('--start-maximized') # 最大化运行(全屏窗口),不设置,取元素会报错
chrome_options.add_argument('--disable-infobars') # 禁用浏览器正在被自动化程序控制的提示
chrome_options.add_argument('--incognito') # 隐身模式(无痕模式)
chrome_options.add_argument('--hide-scrollbars') # 隐藏滚动条,应对一些特殊页面
chrome_options.add_argument('--disable-javascript') # 禁用 javascript
chrome_options.add_argument('--blink-settings=imagesEnabled=false') # 不加载图片,提升速度
chrome_options.add_argument('--headless') # 浏览器不提供可视化页面
chrome_options.add_argument('--ignore-certificate-errors') # 禁用扩展插件并实现窗口最大化
chrome_options.add_argument('--disable-gpu') # 禁用 GPU 加速
chrome_options.add_argument('–disable-software-rasterizer')
chrome_options.add_argument('--disable-extensions') # 禁用扩展
chrome_options.add_argument('--start-maximized') # 最大化
添加扩展应用(add_extension, add_encoded_extension)
# 添加插件 extension_path = '插件路径' chrome_options.add_extension(extension_path)
添加实验性质的设置参数(add_experimental_option)
# 禁止图片加载
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
# 禁用保存密码
prefs = {"": ""}
prefs["credentials_enable_service"] = False
prefs["profile.password_manager_enabled"] = False
chrome_options.add_experimental_option("prefs", prefs)
# 手机模式
option = webdriver.ChromeOptions()
option.add_argument('disable-infobars')
mobile_emulation = {"deviceName": "iPhone 6"}
option.add_experimental_option('mobileEmulation', mobile_emulation)
driver = webdriver.Chrome(chrome_options=option)
禁用弹窗
# 禁用浏览器弹窗
prefs = {
'profile.default_content_setting_values': {
'notifications': 2
}
}
chrome_options.add_experimental_option('prefs', prefs)
截屏
通过搜索文档,可以发现,Selenium 提供了以下两种类型的截屏功能:
[WebElement.screenshot(filename)][WebElement.screenshot]:该方法可以对元素进行截屏,如下代码所示:
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
from selenium.webdriver import ChromeOptions
options = ChromeOptions()
options.add_argument('headless')
with webdriver.Chrome(options=options) as driver:
driver.get('https://www.jianshu.com')
wait = WebDriverWait(driver, 10)
# 文章区域
el = wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, '#list-container')
))
# 浏览器窗口设置为元素大小,以保证能完全截取元素区域
driver.set_window_size(el.size['width'], el.size['height'])
el.screenshot('D:\\jianshu.png')
WebDriver.save_screenshot(filename):截取浏览器当前页面。具体代码如下所示:
options = ChromeOptions()
options.add_argument('headless')
with webdriver.Chrome(options=options) as driver:
driver.get('https://www.jianshu.com')
wait = WebDriverWait(driver, 10)
#文章区域
wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, '#list-container')
))
width = driver.execute_script("return document.documentElement.scrollWidth")
height = driver.execute_script("return document.documentElement.scrollHeight")
print(f'page scroll size: {width}x{height}')
#将窗口设置为页面滚动宽高
driver.set_window_size(width, height)
print('screenshot done') if driver.save_screenshot('D:\\jianshu.png') else print('screenshot failed!!')
注:截屏时我们需要将窗口的宽高设置为元素/页面滚动宽高,这样就可以完整截取整个元素/页面内容,但一个前提是必须使用 Headless模式,否则窃取的只是当前视口高度内容。
Selenium反爬措施
由于使用Selenium可以很方便对网页进行自动化操作,这同时也表示说Selenium是一个非常好用的爬虫处理器。
也因此,有一些网站就增加了对Selenium的检测,以禁止Selenium进行爬虫操作。
目前使用最多的检测Selenium爬取的方式为:通过检测浏览器当前页面的window.navigator,如果其包含有webdriver这个属性,那就表示使用了Selenium进行爬取,反之,如果检测到webdriver为undefined,则表示正常操作。
如果我们直接使用Selenium进行爬取,如下所示:
driver = webdriver.Chrome()
driver.get('https://antispider1.scrape.cuiqingcai.com/')
print(driver.page_source)
运行上述代码后,可以看到:WebddriverForbidden。
而如果想解决这个问题,依据先前我们介绍的原理可知,只要我们能将window.navigator.webdriver设置为undefined即可,但是我们不能简单地调用如下代码进行设置:
driver.execute_script('Object.defineProperty(navigator, "webdriver", {get: () => undefined})')
因为WebDriver.execute_script(..)是在页面加载完成后才执行,在时序上慢于网页检测。
因此,我们的变量修改必须尽早地在页面加载完成时进行设置。
而Selenium中提供的相应机制为:CDP(即Chrome Devtools-Protocol,Chrome开发工具协议)
对应的接口为:WebDriver.execute_cdp_cmd(cmd, cmd_args)
对应于上述示例,如果我们想要尽早在页面加载完成时执行我们的JavaScript脚本,需要使用的CDP命令为:Page.addScriptToEvaluateOnNewDocument,具体的代码如下所示:
options = ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(options=options)
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
})
browser.get('https://antispider1.scrape.cuiqingcai.com/')
参考链接:



