Selenium 是一套 Web网站的程序自动化操作解决方案。通过它,我们可以写出自动化程序,像人一样在浏览器里操作web界面。 比如点击界面按钮,在文本框中输入文字等操作。
支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera和Edge等。这里我将以Chrome为例进行Selenium功能的演示~
不同的编程语言选择不同的Selenium客户端库,对应我们Python语言来说,Selenium客户端库的安装非常简单,用 pip 命令即可。
打开命令行程序,运行如下命令:pip install selenium
网络不好的话,可以使用豆瓣源:pip install selenium -i https://pypi.douban.com/simple/
浏览器驱动是和浏览器对应的。 不同的浏览器需要选择不同的浏览器驱动。
2.1 浏览器的选择
目前主流的浏览器中,谷歌 Chrome 浏览器对Selenium自动化的支持更加成熟一些。
2.2 检查浏览器版本
其实,有两种方式安装浏览器驱动:一种是常见的手动安装,另一种则是利用第三方库自动安装。
这里介绍手动安装,自动安装参见:https://mp.weixin.qq.com/s/_lsDLPpNI1zdSwaPgdTZHQ
首先需要查看浏览器版本(2种方式均可):
方式1:在浏览器的地址栏键入Chrome://version
,即可查看浏览器版本号
方式2:点击“Chrome菜单”→“帮助”→“关于Google Chrome”,查看浏览器版本号
相当于地址栏输入:chrome://settings/help
2.3 浏览器驱动的安装
下载地址:https://chromedriver.storage.googleapis.com/index.html
注意:驱动和浏览器的版本号越接近越好,但是略有差别,比如98和97 ,通常也没有什么问题。
通过调用浏览器驱动,可以打开浏览器,就可以了
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 创建 WebDriver 对象,指明使用chrome浏览器驱动
# 注意,等号右边 返回的是 WebDriver 类型的对象,我们可以通过这个对象来操控浏览器,比如 打开网址、选择界面元素等。
wb = webdriver.Chrome(service=Service(r'C:\Users\asuka\Downloads\Compressed\chromedriver.exe'))
# 添加 input,让程序保持运行状态
input()
在创建WebDriver对象时,需要指定浏览器驱动路径,这样写有2个问题:
有什么好办法呢?我们可以把浏览器驱动 所在目录加入环境变量Path , 写代码时,就可以无需指定浏览器驱动路径了,像这样:wd = webdriver.Chrome()
设置完环境变量后,别忘了重启IDE(比如 PyCharm) 新的环境变量才会生效。
其他报错、报毒问题,参见:原理与安装 | 白月黑羽
其他浏览器,如:360、Edge,参见:https://mp.weixin.qq.com/s/0mCAEcdRdyVKzDrueMUlpw
使用Get方法访问某网站
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 初始化浏览器为chrome浏览器
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
# 访问百度首页
browser.get(r'https://www.baidu.com/')
# 关闭浏览器
browser.close()
get_window_size()
获取窗口大小
set_window_size()
方法可以用来设置浏览器大小(就是分辨率)
maximize_window
则是设置浏览器为全屏!
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
# 获取当前窗口大小
browser.get(r'https://www.baidu.com')
print(browser.get_window_size()) # {'width': 838, 'height': 892}
time.sleep(2)
# 设置浏览器大小:全屏
browser.maximize_window()
time.sleep(2)
# 设置分辨率 500*500
browser.set_window_size(500, 500)
time.sleep(2)
# 设置分辨率 1000*800
browser.set_window_size(1000, 800)
time.sleep(2)
# 关闭浏览器
browser.close()
网上搜的结果不能用,问ChatGPT问出了答案。
代理类型:HTTP代理
方式1:为浏览器驱动设置了环境变量
from selenium import webdriver
# 设置代理IP的地址和端口号,类型为 HTTP 代理
proxy_address = "127.0.0.1:8080"
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--proxy-server=http://" + proxy_address)
# 浏览器驱动访问网站
driver = webdriver.Chrome(options=chrome_options)
driver.get("https://www.baidu.com")
# 关闭浏览器
driver.quit()
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 设置代理IP的地址和端口号,类型为 HTTP 代理
proxy_address = "127.0.0.1:8080"
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--proxy-server=http://" + proxy_address)
# 浏览器驱动访问网站
driver = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'), options=chrome_options)
driver.implicitly_wait(10)
driver.get('https://cn.bing.com/')
# 关闭浏览器
driver.quit()
一些基础属性如:网页标题、网址、浏览器名称、页面源码等信息。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
browser.implicitly_wait(5)
browser.get(r'https://www.baidu.com')
# 获取当前窗口标题
print(browser.title)
# 获取当前窗口URL地址
print(browser.current_url)
# 浏览器名称
print(browser.name)
# 网页源码
print(browser.page_source)
# 关闭浏览器
browser.close()
from selenium import webdriver
# 无界面的浏览器
option = webdriver.ChromeOptions()
option.add_argument("headless")
browser = webdriver.Chrome(options=option)
# 访问百度首页
browser.get(r'https://www.baidu.com/')
# 截图预览
browser.get_screenshot_as_file('截图.png')
# 关闭浏览器
browser.close()
刷新页面是我们在浏览器操作时很常用的操作,这里refresh()
方法可以用来进行浏览器页面刷新。
from selenium import webdriver
import time
browser = webdriver.Chrome()
# 设置浏览器全屏
browser.maximize_window()
browser.get(r'https://www.baidu.com')
time.sleep(2)
try:
# 刷新页面
browser.refresh()
print('刷新页面')
except Exception as e:
print('刷新失败')
# 关闭浏览器
browser.close()
前进后退是我们在使用浏览器时非常常见的操作,这里forward()
方法可以用来实现前进,back()
可以用来实现后退。
from selenium import webdriver
import time
browser = webdriver.Chrome()
# 设置浏览器全屏
browser.maximize_window()
browser.get(r'https://www.baidu.com')
time.sleep(2)
# 打开必应页面
browser.get(r'https://cn.bing.com/')
time.sleep(2)
# 后退到百度页面
browser.back()
time.sleep(2)
# 前进的必应页面
browser.forward()
time.sleep(2)
# 关闭浏览器
browser.close()
测试网站:https://cdn2.byhy.net/files/selenium/sample3.html
实现目标:访问必应网站,执行一次搜索,然后返回到测试网站
这里主要涉及到窗口切换,可以使用:wd.switch_to.window(handle)
,这是一个列表对象,里面包括了当前浏览器里面所有的窗口句柄。所谓句柄,大家可以想象成对应网页窗口的一个ID。
此外,我们可以设置一个当前窗口让它记住,方便随时切回来。
# mainWindow变量保存当前窗口的句柄
mainWindow = wd.current_window_handle
#通过前面保存的老窗口的句柄,自己切换到老窗口
wd.switch_to.window(mainWindow)
得到如下代码:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/sample3.html')
# 点击按钮,打开必应
link = wd.find_element(By.TAG_NAME, 'a')
link.click()
time.sleep(3)
# 记住当前窗口
mainWindow = wd.current_window_handle
# 找到必应窗口
for hanle in wd.window_handles:
wd.switch_to.window(hanle) # 切窗口
if 'Bing' in wd.title: # 发现某窗口的网页标题中有“Bing”
break # 找到了
print(wd.title) # 输出切到的新窗口的title
# 开始搜索
wd.find_element(By.ID, 'sb_form_q').send_keys('白月黑羽\n')
time.sleep(3)
# 回退到之前记住的窗口
wd.switch_to.window(mainWindow)
time.sleep(3)
wd.close()
有些网站点击的时候,会弹出的对话框。那么如果点出弹框,获取弹框内容,以及关闭弹框呢?
测试网站:https://cdn2.byhy.net/files/selenium/test4.html
Alert弹框
Alert 弹出框,目的就是显示通知信息,只需用户看完信息后,点击 OK(确定) 就可以了。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- alert ---
wd.find_element(By.ID, 'b1').click()
# 打印 弹出框 提示信息
print(wd.switch_to.alert.text)
time.sleep(3)
# 点击 OK 按钮
wd.switch_to.alert.accept()
time.sleep(3)
wd.quit()
Confirm弹框
Confirm弹出框,主要是让用户确认是否要进行某个操作
# 如果我们想点击 OK 按钮,可以用 accept 方法
driver.switch_to.alert.accept()
# 如果我们想点击 Cancel 按钮, 可以用 dismiss 方法
driver.switch_to.alert.dismiss()
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- confirm ---
wd.find_element(By.ID, 'b2').click()
# 打印 弹出框 提示信息
print(wd.switch_to.alert.text)
# 点击 OK 按钮
wd.switch_to.alert.accept()
# --- confirm ---
wd.find_element(By.ID, 'b2').click()
# 点击 取消 按钮
wd.switch_to.alert.dismiss()
time.sleep(3)
wd.quit()
Prompt弹框
出现 Prompt 弹出框 是需要用户输入一些信息,提交上去。
比如:当管理员在网站上选择给某个账号延期时,就可能会弹出 Prompt 弹出框, 要求输入延期多长时间。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- prompt ---
wd.find_element(By.ID, 'b3').click()
# 获取 alert 对象
alert = wd.switch_to.alert
# 打印 弹出框 提示信息
print(alert.text)
# 输入信息,并且点击 OK 按钮 提交
alert.send_keys('web自动化 - selenium')
alert.accept()
# 点击 Cancel 按钮 取消
wd.find_element(By.ID, 'b3').click()
alert = wd.switch_to.alert
alert.dismiss()
time.sleep(3)
wd.quit()
当鼠标放到某个位置,会出现一些内容,如果想获取这些内容对应的位置,发现 F12不好用了,甚至有些时候通过“审查元素”都找不到(或者只能找到1个,不方便找其他的,如下图中的“影响推广”),这个时候就需要冻结界面了。
我们可以在控制台中输入debugger
实现冻结效果。
那么,如果延迟数秒再冻结,就可以解决悬停显示问题了:
# 在 5000毫秒后,执行 debugger 命令
# debug状态有个特性,界面被冻住,不管我们怎么点击界面都不会触发事件。
setTimeout(function(){debugger}, 5000)
选择到元素之后,我们的代码会返回元素对应的 WebElement对象,通过这个对象,我们就可以操控元素了。
操控元素通常包括:
点击元素非常简单,就是调用元素WebElement对象的click
方法。前面我们已经学过。
这里我们要补充讲解一点。当我们调用 WebElement 对象的click
方法去点击元素的时候, 浏览器接收到自动化命令,点击的是该元素的中心点位置。
element = wd.find_element(By.ID,'go')
element.click()
演示如何输入一个内容,点击按钮,等待加载结果
假设要获取搜索结果中的第一个结果内容,这里要添加一个休眠时间,因为搜索出结果是需要时间的!
我想的是挺好的,但是代码一跑就出错了,抛了异常:NoSuchElementException,意思就是在当前的网页上 找不到该元素, 就是找不到 id 为 1 的元素。
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://www.byhy.net/_files/stock1.html')
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯\n')
element = wd.find_element(By.ID, '1')
print(element.text)
wd.quit()
出现这个问题的原因在于网站还没有来得及返回搜索结果,代码就索要结果了。因此代码需要等待网站搜索出结果才行,那要等多久呢?直接设置个sleep休眠显然不好,不如让代码反复try:
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://www.byhy.net/_files/stock1.html')
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯\n')
while True:
try:
element = wd.find_element(By.ID, '1')
print(element.text)
break
except:
# 添加一个休眠,防止网站响应慢导致程序进入死循环
time.sleep(1)
wd.quit()
# 结果
国美通讯
代码:600898
其实,Selenium提供了一个更合理的解决方案,是这样的:
当发现元素没有找到的时候, 并不立即返回找不到元素的错误。而是周期性(每隔半秒钟)重新寻找该元素,直到该元素找到,或者超出指定最大等待时长,这时才抛出异常(如果是 find_elements 之类的方法, 则是返回空列表)。
Selenium 的 Webdriver 对象有个方法叫implicitly_wait
,可以称之为“隐式等待”,或者“全局等待”。该方法接受一个参数,用来指定最大等待时长。如果我们加入如下代码:wd.implicitly_wait(10)
,那么后续所有的find_element
或者find_elements
之类的方法调用都会采用上面的策略。如果找不到元素, 每隔半秒钟再去界面上查看一次, 直到找到该元素, 或者过了10秒最大时长。
特别警告⚠:implicitly_wait
是为了解决找不到的问题,但是找到的也有可能是错误的。
当你网购时选择收件地址,选择省、市、县……,你怎么切换都有结果,但是你需要等“市”加载完之后,才能选择“县”,这个时候implicitly_wait
就不好使了,还是需要sleep
一下。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.implicitly_wait(10) # 最久等待10秒
wd.get('https://www.byhy.net/_files/stock1.html')
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯\n')
# 返回页面 ID 为 1 的元素
element = wd.find_element(By.ID, '1')
print(element.text)
wd.quit()
# 结果
国美通讯
代码:600898
使用WebElement对象的clear
方法清空输入框内容
使用WebElement对象的send_keys
方法输入内容
测试靶场:https://cdn2.byhy.net/files/selenium/test3.html
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
# 设置浏览器全屏
browser.maximize_window()
browser.get(r'https://cdn2.byhy.net/files/selenium/test3.html')
element = browser.find_element(By.ID, 'input1')
element.clear() # 清除输入框已有的字符串
element.send_keys('白月黑羽\n') # 输入新字符串
input()
上面已经知道,通过WebElement对象的 text 属性,可以获取元素展示在界面上的文本内容。
element = wd.find_element(By.ID, '1')
print(element.text)
对于input输入框的元素,要获取里面的输入文本,用text属性是不行的,这时可以使用element.get_attribute(‘value’)
测试靶场:https://www.byhy.net/_files/stock1.html
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
browser.get(r'https://www.byhy.net/_files/stock1.html')
element = browser.find_element(By.ID, 'kw')
element.send_keys('白月黑羽')
print(element.get_attribute('value'))
browser.close()
# 结果
白月黑羽
但是,有时候,元素的文本内容没有展示在界面上,或者没有完全完全展示在界面上。这时,用WebElement对象的text属性,获取文本内容,就会有问题。出现这种情况,可以尝试使用 element.get_attribute(‘innerText’) ,或者 element.get_attribute(‘textContent’)。
使用 innerText 和 textContent 的区别是,前者只显示元素可见文本内容,后者显示所有内容(包括display属性为none的部分)。具体可以参考这里
通过WebElement对象的 get_attribute 方法来获取元素的属性值,比如要获取元素属性class的值,就可以使用 element.get_attribute(‘class’)
练习靶场:https://www.byhy.net/_files/stock1.html
练习目标:获取“包钢股份”的属性值
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
# 设置浏览器全屏
browser.maximize_window()
browser.get(r'https://www.byhy.net/_files/stock1.html')
element = browser.find_element(By.ID, '1')
print(element.text + '\n')
print(element.get_attribute('class'))
input()
# 结果
包钢股份
代码:600010
result-item
要获取整个元素对应的HTML文本内容,可以使用element.get_attribute('outerHTML')
如果,只是想获取某个元素内部的HTML文本内容,可以使用element.get_attribute('innerHTML')
测试靶场:https://www.byhy.net/_files/stock1.html
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
browser.get(r'https://www.byhy.net/_files/stock1.html')
element = browser.find_element(By.ID, '1')
# 获取整个元素对应的HTML文本内容
print(element.get_attribute('outerHTML'))
# 获取某个元素内部的HTML文本内容
print(element.get_attribute('innerHTML'))
browser.close()
获取frame元素或者iframe元素的内部数据时,需要先切入进去才能获取。
测试网站:https://cdn2.byhy.net/files/selenium/sample2.html
假设我们要获取所有的植物,但是会发现下面的代码运行结果为空。仔细观察源代码会发现,要找的植物位于iframe中。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample2.html')
# 根据 class name 选择元素,返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')
for element in elements:
print(element.text)
在html语法中,frame元素或者iframe元素的内部会包含一个被嵌入的另一份html文档。在我们使用selenium打开一个网页时,我们的操作范围缺省是当前的html,并不包含被嵌入的html文档里面的内容。如果我们要操作被嵌入的html文档中的元素,就必须切换操作范围到被嵌入的文档中。
使用 WebDriver 对象的 switch_to 属性进行切换,像这样:wd.switch_to.frame(frame_reference)
其中, frame_reference 可以是 frame 元素的属性 name 或者 ID。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample2.html')
# 切换到iframe中
wd.switch_to.frame('frame1')
elements = wd.find_elements(By.CLASS_NAME, 'plant')
for element in elements:
print(element.text)
# 结果
土豆
洋葱
白菜
还可以通过 frame 所对应的 WebElement 对象切换进去:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample2.html')
# 切换到iframe中
element = wd.find_element(By.CSS_SELECTOR, 'iframe[src="sample1.html"]')
wd.switch_to.frame(element)
elements = wd.find_elements(By.CLASS_NAME, 'plant')
for element in elements:
print(element.text)
# 结果
土豆
洋葱
白菜
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/sample2.html')
# 切换到iframe中,获取植物
# element = wd.find_element(By.CSS_SELECTOR, 'iframe[src="sample1.html"]')
# wd.switch_to.frame(element)
wd.switch_to.frame('frame1')
elements = wd.find_elements(By.CLASS_NAME, 'plant')
for element in elements:
print(element.text)
# 切出,回到最外部的HTML中
wd.switch_to.default_content()
# 点击外部的一个按钮
wd.find_element(By.ID, 'outerbutton').click()
# 获取点击出来的内容
elements = wd.find_elements(By.CSS_SELECTOR, '#add li')
for element in elements:
print(element.get_attribute('outerHTML'))
time.sleep(2)
wd.quit()
# 结果
土豆
洋葱
白菜
<li>你点击了外部按钮</li>
这里需要导入ActionChains 类:
from selenium.webdriver.common.action_chains import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://www.baidu.com/')
from selenium.webdriver.common.action_chains import ActionChains
ac = ActionChains(wd)
# 获取移动的位置 对应的元素
element = wd.find_element(By.CSS_SELECTOR, '[name="tj_briicon"]')
# 移动到指定位置
ac.move_to_element(element).perform()
time.sleep(3)
wd.quit()
左键就是click()
,很多地方都有演示。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10) # 最久等待10秒
wd.get('https://www.byhy.net/_files/stock1.html')
time.sleep(2)
# 输入
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯')
time.sleep(2)
# 点击
element = wd.find_element(By.ID, 'go')
element.click()
time.sleep(2)
wd.quit()
鼠标右键:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://www.baidu.com/')
# 定位到要右击的元素,这里选的新闻链接
right_click = wd.find_element(By.LINK_TEXT, '新闻')
# 执行鼠标右键操作
ActionChains(wd).context_click(right_click).perform()
time.sleep(2)
# 关闭浏览器
wd.close()
使用方法:drag_and_drop(source,target)
演示靶场:https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
import time
browser = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
browser.implicitly_wait(10)
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
time.sleep(2)
# 切入 iframe
browser.switch_to.frame('iframeResult')
# 开始位置
source = browser.find_element(By.CSS_SELECTOR, '#draggable')
# 结束位置
target = browser.find_element(By.CSS_SELECTOR, "#droppable")
# 执行元素的拖放操作
ActionChains(browser).drag_and_drop(source, target).perform()
time.sleep(5)
# 关闭浏览器
browser.close()
在selenium自动化中,最最常用的操作就是点击和输入,要实现这个操作,经常会碰到的一个问题就是怎么找到点击和输入的位置?有以下几种方法帮助我们找到这些位置。
记不全没关系,下面有好用的CSS表达式
我们可以把 id 想象成元素的编号, 是用来在html中标记该元素的。根据规范, 如果元素有id属性 ,这个id 必须是当前html中唯一的。所以如果元素有id, 根据id选择元素是最简单高效的方式。
练习靶场:https://www.byhy.net/_files/stock1.html
实现目标:查询股票代码
id
为kw
的元素。浏览器 找到id
为kw
的元素后,将结果通过 浏览器驱动 返回给 自动化程序, 所以 find_element 方法会返回一个 WebElement 类型的对象。这个WebElement 对象可以看成是对应 页面元素的遥控器。我们通过这个WebElement对象,就可以 操控对应的界面元素。from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建 WebDriver 对象
wd = webdriver.Chrome()
# 调用WebDriver 对象的get方法 可以让浏览器打开指定网址
wd.get('https://www.byhy.net/_files/stock1.html')
# 根据id选择元素,返回的就是该元素对应的WebElement对象
element = wd.find_element(By.ID, 'kw')
# 通过该 WebElement对象,就可以对页面元素进行操作了
# 比如输入字符串到 这个 输入框里
element.send_keys('通讯')
element = wd.find_element(By.ID,'go')
element.click()
# 正常结束程序
wd.quit()
web自动化的难点和重点之一,就是如何 选择 我们想要操作的web页面元素。
除了根据元素的id ,我们还可以根据元素的 class 属性选择元素。
练习靶场:https://cdn2.byhy.net/files/selenium/sample1.html
练习根据class属性选择元素
可以看到,所有的植物元素都有个class属性值为 plant,所有的动物元素都有个class属性值为 animal。
假设我要获取所有的动物:
于是得到脚本:
from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
wd = webdriver.Chrome()
# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据 class name 选择元素,返回的是 一个列表
# 里面 都是class 属性值为 animal的元素对应的 WebElement对象
elements = wd.find_elements(By.CLASS_NAME, 'animal')
# 取出列表中的每个 WebElement对象,打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
print(element.text)
# 结果
狮子
老虎
山羊
注意事项:
如果把wd.find_elements(By.CLASS_NAME, 'animal')
改为wd.find_element(By.CLASS_NAME, 'animal')
,那么返回的就是第一个class 属性为 animal 的元素, 也就是“狮子”
练习靶场:https://cdn2.byhy.net/files/selenium/sample1.html
我们可以通过指定参数为By.TAG_NAME
,选择所有的tag名为 div 的元素,如下所示:
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据 tag name 选择元素,返回的是 一个列表
# 里面 都是 tag 名为 div 的元素对应的 WebElement对象
elements = wd.find_elements(By.TAG_NAME, 'div')
# 取出列表中的每个 WebElement对象,打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
print(element.text)
升级一下,混合 id 属性和 tag 属性,来获取结果。
目标:获取红框中的数据
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
element = wd.find_element(By.ID, 'container')
# 限制 选择元素的范围是 id 为 container 元素的内部。
spans = element.find_elements(By.TAG_NAME, 'span')
for span in spans:
print(span.text)
# 结果
内层11
内层12
内层21
在后面学习了CSS表达式后,可以通过获取后代元素的方式简化代码:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
elements = wd.find_elements(By.CSS_SELECTOR, '#container span')
for element in elements:
print(element.text)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://www.baidu.com/')
# 使用CSS表达式
# wd.find_element(By.CSS_SELECTOR, '[href="http://news.baidu.com"]').click()
# 使用基本方法
wd.find_element(By.LINK_TEXT, '新闻').click()
time.sleep(6)
wd.close()
有时候一个超链接的文本很长,我们如果全部输入,既麻烦,又显得代码很不美观,这时候我们就可以只截取一部分字符串,用这种方法模糊匹配了。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://www.baidu.com/')
# 模糊匹配“新闻”中的部分字符
wd.find_element(By.PARTIAL_LINK_TEXT, '闻').click()
time.sleep(6)
wd.close()
如果我们要选择的元素没有id、class属性,或者有些我们不想选择的元素也有相同的id、class属性值,怎么办呢?这时候我们通常可以通过CSS selector语法选择元素。这是一种很强大好用的选择方法。
练习靶场:https://cdn2.byhy.net/files/selenium/sample1.html
CSS Selector同样可以根据tag名、id属性和class属性来选择元素。
1.1 根据tag名选择元素的CSS Selector语法非常简单,直接写上tag名即可,比如要选择所有的tag名为div的元素,就可以是这样,下面第8行和第9行代码功能是一样的。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# elements = wd.find_elements(By.TAG_NAME, 'div')
elements = wd.find_elements(By.CSS_SELECTOR, 'div')
for element in elements:
print(element.text)
wd.close()
1.2 根据id属性选择元素的语法是在id号前面加上一个井号: #id值
尝试在下面的输入框中输入内容
下面第8行和第9行代码功能是一样的。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# element = wd.find_element(By.ID, 'searchtext')
element = wd.find_element(By.CSS_SELECTOR, '#searchtext')
element.send_keys('你好啊')
wd.close()
1.3 根据class属性选择元素的语法是在 class 值 前面加上一个点: .class值
下面第8行和第9行代码功能是一样的。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# elements = wd.find_elements(By.CLASS_NAME, 'animal')
elements = wd.find_elements(By.CSS_SELECTOR, '.animal')
for element in elements:
print(element.text)
# 结果
狮子
老虎
山羊
css选择器专门提供了常用属性 id、class 选择的语法,那么其他的属性呢?
css 选择器支持通过任何属性来选择元素,语法是用一个方括号 [] 。
举例:我想获取这个链接地址
测试代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
elements = wd.find_elements(By.CSS_SELECTOR, '[href="http://www.miitbeian.gov.cn"]')
for element in elements:
print(element.get_attribute('outerHTML'))
wd.quit()
# 结果
<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
CSS还可以选择属性值包含某个字符串的元素比如,要选择a节点,里面的href属性包含了miitbeian字符串,就可以这样写:a[href*="miitbeian"]
# 还可以选择属性值以某个字符串开头的元素。
# 比如,要选择a节点,里面的href属性以http开头,就可以这样写
a[href^="http"]
# 还可以选择属性值以某个字符串结尾的元素。比如,
# 要选择a节点,里面的href属性以gov.cn结尾,就可以这样写
a[href$="gov.cn"]
练习靶场:https://cdn2.byhy.net/files/selenium/sample1.html
网页代码如下所示:
<div id='container'>
<div id='layer1'>
<div id='inner11'>
<span>内层11span>
div>
<div id='inner12'>
<span>内层12span>
div>
div>
<div id='layer2'>
<div id='inner21'>
<span>内层21span>
div>
div>
div>
上面的说法看懂看不懂无所谓,代码演示一下就懂了:
使用右箭头获取直接子元素
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
elements = wd.find_elements(By.CSS_SELECTOR, '#container div')
for element in elements:
print(element.get_attribute('outerHTML'))
print('-----------------')
wd.quit()
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
elements = wd.find_elements(By.CSS_SELECTOR, '.plant span')
for element in elements:
print(element.get_attribute('outerHTML'))
print(element.text)
print('-----------------')
wd.quit()
验证CSS Selector
通过在浏览器中输入CSS Selector语法,可以很方便的判断语法是否正确,是否符合效果。
避免了在开发工具中反复调试代码。F12 ➡ Ctrl+F
进一步精准定位:#bottom > .footer1 span.copyright
**逻辑或:**使用逗号进行分割,运行优先级低
案例1:获取所有的动物和植物
测试网站:https://cdn2.byhy.net/files/selenium/sample1.html
使用“逻辑或”直接获取:.plant,.animal
案例2:获取唐诗中的所有作品和作者
测试网站:https://cdn2.byhy.net/files/selenium/sample1a.html
由于“逻辑或”的优先级低,因此使用:#t1 span , #t1 p
父元素的第n个子节点
我们可以指定选择的元素是父元素的第几个子节点,使用nth-child
案例1:
测试网站:https://cdn2.byhy.net/files/selenium/sample1b.html
使用:span:nth-child(2)
,获取第2个子元素是span的数据
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/sample1b.html')
elements = wd.find_elements(By.CSS_SELECTOR, 'span:nth-child(2)')
for element in elements:
print(element.get_attribute('outerHTML'))
wd.quit()
# 结果
<span>李白</span>
<span>苏轼</span>
案例2:由于没有指明第2个子节点的标签名,因此所有的第2个子节点都会被选上。:nth-child(2)
这个东西可以正着数,也可以倒着数,如:父元素的倒数第n个子节点
使用:nth-last-child
测试网站:https://cdn2.byhy.net/files/selenium/sample1b.html
目标:获取父元素倒数第1个子节点是p类型的:p:nth-last-child(1)
父元素的第几个某类型的子节点
我们可以指定选择的元素是父元素的第几个某类型的子节点,使用nth-of-type
举例如下,p:nth-of-type(2)
匹配的子节点类型是p
,这个p
是第二个。
可以通俗的理解为,要从闺女(span)、侄子(span)、儿子(p)中寻找二儿子。
这个东西可以正着数,也可以倒着数,如:父元素的倒数第几个某类型的子节点
p:nth-last-of-type(2)
:获取子节点是p
的,倒数第二个p
如果要选择的是父元素的 偶数节点,使用 nth-child(even)
如果要选择的是父元素的 奇数节点,使用 nth-child(odd)
相邻兄弟节点选择,使用+
进行选择
p + span
:p后面的同级别节点,第一个是span的就选上
后续所有兄弟节点选择,使用~
进行选择
h3 ~ span
:h3后面的同级别节点,只要是span,就全选上
更多CSS选择器的介绍,可以参考 CSS 选择器参考手册
上面已经学习了CSS,为什么还要学习 Xpath呢? 因为:
测试网站:https://cdn2.byhy.net/files/selenium/test1.html
**绝对路径:**从根节点开始,到某个节点,每层都依次写下来,每层之间用 / 分隔的表达式。
可以通过浏览器直接复制出绝对路径,十分方便,假设我这里要获取所有的城市。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(5)
wd.get(r'https://cdn2.byhy.net/files/selenium/test1.html')
elements = wd.find_elements(By.XPATH,'/html/body/div/div/div//p')
for element in elements:
print(element.text)
wd.close()
# 结果
北京
上海
纽约
休斯顿
芝加哥
**相对路径:**用//
开始,选择内部直接子节点使用/
,选择内部所有子节点使用//
我还是习惯使用绝对路径,然后微调获取所有的外国城市://div/span/p
如果要选择所有div节点的所有直接子节点,可以使用表达式//div/*
*
是一个通配符,对应任意节点名的元素,等价于CSS选择器div > *
代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(5)
wd.get(r'https://cdn2.byhy.net/files/selenium/test1.html')
elements = wd.find_elements(By.XPATH, '//div/*')
for element in elements:
print(element.get_attribute('outerHTML'))
wd.close()
Xpath 可以根据属性来选择元素。
根据属性来选择元素是通过这种格式来的 [@属性名=‘属性值’]
注意:
@
方式1:根据id属性选择
选择 id 为 west 的元素,可以这样//*[@id='west']/p
方式2:根据class属性选择
选择所有 select 元素中 class 为 single_choice 的元素,可以这样//select[@class='single_choice']
方式3:其他属性
比如选择具有 multiple 属性的所有页面元素 ,可以这样//*[@multiple]
(因为没有值,所以只写属性)
方式4:包含某字符
要选择 style 属性值包含 color 字符串的页面元素,可以这样//*[contains(@style,'color')]
要选择 style 属性值以 color 字符串 开头 的页面元素 ,可以这样//*[starts-with(@style,'color')]
要选择 style 属性值以某个字符串结尾的页面元素,大家推测是 //*[ends-with(@style,'color')]
, 但是很遗憾,这是xpath 2.0 的语法 ,目前浏览器支持的是 xpath 1的语法。
//p[2]
注意,选择的是 p 类型第2个的子元素;并非选择第2个子元素类型是p。
//div/*[2]
//p[last()]
//p[last()-1]
xpath还可以选择子元素的次序范围。
//option[position()<=2]
或者//option[position()<3]
//*[@class='multi_choice']/*[position()<=3]
//*[@class='multi_choice']/*[position()>=last()-2]
为什么不是 last()-3 呢? 因为:
last() 本身代表最后一个元素
last()-1 本身代表倒数第2个元素
last()-2 本身代表倒数第3个元素
逻辑或
css有组选择(这里我称为“逻辑或”),可以同时使用多个表达式,多个表达式选择的结果都是要选择的元素。
比如,要选所有的option元素 和所有的 h4 元素,可以使用:
Xpath选择的语法
//option | //h4
等同于CSS选择器
option , h4
再比如,要选所有的 class属性为 single_choice 和 class属性为 multi_choice 的元素,可以使用:
Xpath选择的语法
//*[@class='single_choice'] | //*[@class='multi_choice']
等同于CSS选择器
.single_choice , .multi_choice
选择父节点⭐
Xpath可以选择父节点, 这是css做不到的。某个元素的父节点用 /..
表示。
如果某个元素没有特征,但是它的子节点有特征, 就可以采用这种方法。
比如,要选择 id 为 china 的节点的父节点,可以这样写//*[@id='china']/..
后续兄弟节点
前面学过css选择器,要选择某个节点的后续兄弟节点,用波浪线。
Xpath也可以选择后续兄弟节点,用这样的语法following-sibling::
比如,要选择 class 为 single_choice 的元素的所有后续兄弟节点
Xpath选择的语法
//*[@class='single_choice']/following-sibling::*
等同于CSS选择器
.single_choice ~ *
前面兄弟节点
xpath还可以选择前面的兄弟节点,用这样的语法preceding-sibling::
要选择 class 为 single_choice 的元素的 所有 前面的兄弟节点,这样写
//*[@class='single_choice']/preceding-sibling::*
要选择 class 为 single_choice 的元素的前面最靠近的兄弟节点 , 这样写
//*[@class='single_choice']/preceding-sibling::*[1]
前面第2靠近的兄弟节点 , 这样写
//*[@class='single_choice']/preceding-sibling::*[2]
而 CSS选择器 目前还没有方法选择 前面的 兄弟节点!
更多Xpath语法,参见:XPath 教程
常见的选择框包括: radio框、checkbox框、select框
测试网址:https://cdn2.byhy.net/files/selenium/test2.html
请注意,演示的网站存在问题,点选其他框后,check不会发生变化,因此不再获取点选之后的结果了。
目标:先打印当前选中的老师名字,再选择小雷老师
网页代码:
<div id="s_radio">
<input type="radio" name="teacher" value="小江老师">小江老师<br>
<input type="radio" name="teacher" value="小雷老师">小雷老师<br>
<input type="radio" name="teacher" value="小凯老师" checked="checked">小凯老师
div>
开发代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test2.html')
# 获取当前选中的元素
element = wd.find_element(By.CSS_SELECTOR, '#s_radio input[checked="checked"]')
print('当前选中的是:' + element.get_attribute('value'))
time.sleep(3)
# 点选 小雷 老师
wd.find_element(By.CSS_SELECTOR, '#s_radio input[value="小雷老师"]').click()
time.sleep(3)
wd.quit()
这种框可以多选。如果某个选项已经勾选了,再点击会取消选择。
目标:只勾选小雷老师
思路:先把已经选中的选项全部点击一下,然后再单独点击目标按钮
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test2.html')
# 获取当前选中的元素,由于可能有多个按钮被选中,因此使用 find_elements
elements = wd.find_elements(By.CSS_SELECTOR, '#s_checkbox input[checked="checked"]')
for element in elements:
print('当前选中的是:' + element.get_attribute('value'))
element.click()
time.sleep(3)
# 点选 小雷 老师
wd.find_element(By.CSS_SELECTOR, '#s_checkbox input[value="小雷老师"]').click()
time.sleep(3)
wd.quit()
radio框及checkbox框都是input元素,只是里面的type不同而已。select框则是一个新的select标签,Selenium专门提供了一个Select类进行操作。Select类提供了如下的方法:
# 根据选项的value属性值,选择元素
select_by_value
# 根据选项的次序(从1开始),选择元素
select_by_index
# 根据选项的可见文本,选择元素
select_by_visible_text
# 根据选项的value属性值,去除选中元素
deselect_by_value
# 根据选项的次序,去除选中元素
deselect_by_index
# 根据选项的可见文本,去除选中元素
deselect_by_visible_text
# 去除选中所有元素
deselect_all
Select单选框操作比较简单:不管原来选的是什么,直接用Select方法重新选择即可。
例如,选择示例里面的小雷老师,示例代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import Select
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.implicitly_wait(10)
wd.get('https://cdn2.byhy.net/files/selenium/test2.html')
element = wd.find_element(By.ID, 'ss_single')
select = Select(element) # 创建Select对象
# 通过 Select 对象选中小雷老师
select.select_by_visible_text("小江老师")
time.sleep(3)
wd.quit()
用select类的deselect_all
方法,全部取消勾选
用select_by_visible_text
方法选择 小雷老师 和 小凯老师
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import Select
import time
wd = webdriver.Chrome(service=Service(r'D:\tools\selenium\chromedriver.exe'))
wd.get('https://cdn2.byhy.net/files/selenium/test2.html')
# 创建Select对象
element = wd.find_element(By.CSS_SELECTOR, '#ss_multi')
select = Select(element)
# 清除所有 已经选中 的选项
select.deselect_all()
# 选择小雷老师 和 小凯老师
select.select_by_visible_text("小雷老师")
select.select_by_visible_text("小凯老师")
time.sleep(3)
wd.quit()
2万字带你了解Selenium全攻略
2万字带你了解Selenium全攻略_selenium 2万字 教程_可以叫我才哥的博客-CSDN博客
原理与安装 | 白月黑羽