pip install selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
# 打开Chome浏览器
wd = webdriver.Chrome()
wd.get('https://www.baidu.com')
# 关闭浏览器
wd.quit()
# 根据id选择元素,返回的就是该元素对应的WebElement对象
element = wd.find_element(By.ID, 'kw')
# 通过该 WebElement对象,就可以对页面元素进行操作了
# 比如输入字符串到 这个 输入框里
element.send_keys('通讯\n')
举例:
<body>
<div class="plant"><span>土豆span>div>
<div class="plant"><span>洋葱span>div>
<div class="plant"><span>白菜span>div>
<div class="animal"><span>狮子span>div>
<div class="animal"><span>老虎span>div>
<div class="animal"><span>山羊span>div>
body>
如果我们要选择 所有的 动物, 就像下面可以这样写
【注意element后面多了个s】
wd.find_elements(By.CLASS_NAME, 'animal')
<span class="chinese student">张三span>
我们要用代码选择这个元素,可以指定任意一个class 属性值,都可以选择到这个元素,如下
element = wd.find_elements(By.CLASS_NAME,'chinese')
或者
element = wd.find_elements(By.CLASS_NAME,'student')
而不能这样写
element = wd.find_elements(By.CLASS_NAME,'chinese student')
类似的,我们可以通过指定 参数为 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)
WebElement对象 也可以调用 find_elements
, find_element
之类的方法。
WebDriver 对象 选择元素的范围是 整个 web页面, 而WebElement 对象 选择元素的范围是 该元素的内部。
element = wd.find_element(By.ID,'container')
# 限制 选择元素的范围是 id 为 container 元素的内部。
spans = element.find_elements(By.TAG_NAME, 'span')
for span in spans:
print(span.text)
使用 find_elements
选择的是符合条件的 所有
元素, 如果没有符合条件的元素, 返回空列表
使用 find_element
选择的是符合条件的 第一个
元素, 如果没有符合条件的元素, 抛出 NoSuchElementException 异常
在我们进行网页操作的时候, 有的元素内容不是可以立即出现的, 可能会等待一段时间,这时候找不到元素就会报错。
import time
while True:
try:
element = wd.find_element(By.ID,'1')
print(element.text)
break
except:
time.sleep(1)
Selenium提供了一个更合理的解决方案,是这样的:
当发现元素没有找到的时候, 并不立即返回 找不到元素的错误。
而是周期性(每隔半秒钟)重新寻找该元素,直到该元素找到,
或者超出指定最大等待时长,这时才 抛出异常(如果是 find_elements
之类的方法, 则是返回空列表)。
Selenium 的 Webdriver 对象 有个方法叫 implicitly_wait
,可以称之为 隐式等待
,或者 全局等待
。
该方法接受一个参数, 用来指定 最大等待时长。
如果我们 加入如下代码
wd.implicitly_wait(10)
那么后续所有的 find_element
或者 find_elements
之类的方法调用 都会采用上面的策略:
如果找不到元素, 每隔 半秒钟 再去界面上查看一次, 直到找到该元素, 或者 过了10秒 最大时长。
完整案例如下
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.implicitly_wait(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)
如果某个控件比较特殊,需要更长的时间加载,比如十几秒或者更长,就可以使用显示等待对其进行单独处理。
WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
driver:浏览器驱动
timeout:最长超时时间,默认以秒为单位
poll_frequency:检测的间隔步长,默认为0.5s
ignored_exceptions:超时后的抛出的异常信息,默认抛出NoSuchElementExeception异常。
需要引入
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait()中的until()和until_not()
#调用该方法提供的驱动程序作为参数,直到返回值为True
WebDriverWait(driver,10).until(method,message="")
#调用该方法提供的驱动程序作为参数,直到返回值为False
WebDriverWait(driver,10).until_not(method,message="")
until是当某元素出现或什么条件成立则继续执行,
until_not是当某元素消失或什么条件不成立则继续执行,参数也相同。
实例:等待图片元素加载完成再进行点击关闭操作
wait = WebDriverWait(driver, 20)
element = wait.until(EC.visibility_of_element_located((By.XPATH, '//img[@src="https://static.cdninstagram.com/rsrc.php/v3/yb/r/sHkePOqEDPz.gif"]')))
time.sleep(1)
driver.find_element(by=By.CSS_SELECTOR, value='svg[aria-label="Close"]').click()
操控元素通常包括
当我们调用 WebElement 对象的 click 方法去点击 元素的时候, 浏览器接收到自动化命令,点击的是该元素的
中心点
位置 。
element = wd.find_element(By.ID, "input1")
element.clear() # 清除输入框已有的字符串
element.send_keys('alkaid') # 输入新字符串
通过WebElement对象的 text
属性,可以获取元素 展示在界面上的
文本内容。
比如
element = wd.find_element(By.ID, 'animal')
print(element.text)
通过WebElement对象的 get_attribute
方法来获取元素的属性值
比如要获取元素属性class的值,就可以使用 element.get_attribute('class')
如下:
element = wd.find_element(By.ID, 'input_name')
print(element.get_attribute('class'))
要获取整个元素对应的HTML文本内容,可以使用 element.get_attribute('outerHTML')
如果,只是想获取某个元素 内部
的HTML文本内容,可以使用 element.get_attribute('innerHTML')
对于input输入框的元素,要获取里面的输入文本,用text属性是不行的,这时可以使用 element.get_attribute('value')
比如
element = wd.find_element(By.ID, "input1")
print(element.get_attribute('value')) # 获取输入框中的文本
通过WebElement对象的 text
属性,可以获取元素 展示在界面上的
文本内容。
但是,有时候,元素的文本内容没有展示在界面上,或者没有完全完全展示在界面上。 这时,用WebElement对象的text属性,获取文本内容,就会有问题。
出现这种情况,可以尝试使用 element.get_attribute('innerText')
,或者 element.get_attribute('textContent')
使用 innerText 和 textContent 的区别是,前者只显示元素可见文本内容,后者显示所有内容(包括display属性为none的部分)
通过 CSS Selector 选择单个元素的方法是
find_element(By.CSS_SELECTOR, CSS Selector参数)
选择所有元素的方法是
find_elements(By.CSS_SELECTOR, CSS Selector参数)
根据tag名选择元素:
比如 要选择 所有的tag名为div的元素,就可以是这样
elements = wd.find_elements(By.CSS_SELECTOR, 'div')
等价于
elements = wd.find_elements(By.TAG_NAME, 'div')
根据id属性选择元素:
语法是在id号前面加上一个井号: #id值
element = wd.find_element(By.CSS_SELECTOR, '#id名')
根据class属性选择元素:
语法是在 class 值 前面加上一个点: .class值
比如 这个网址 https://cdn2.byhy.net/files/selenium/sample1.html
要选择所有 class 属性值为 animal的元素 动物 除了这样写
elements = wd.find_elements(By.CLASS_NAME, 'animal')
还可以这样写
elements = wd.find_elements(By.CSS_SELECTOR, '.animal')
因为是选择 所有
符合条件的 ,所以用 find_elements
而不是 find_element
F12,点击 Elements 标签后, 同时按 Ctrl 键 和 F 键
元素2
是 元素1
的 直接子元素, CSS Selector 选择子元素的语法是这样的元素1 > 元素2
中间用一个大于号 (我们可以理解为箭头号)
注意,最终选择的元素是 元素2, 并且要求这个 元素2 是 元素1 的直接子元素
也支持更多层级的选择, 比如
元素1 > 元素2 > 元素3 > 元素4
就是选择 元素1
里面的子元素 元素2
里面的子元素 元素3
里面的子元素 元素4
, 最终选择的元素是 元素4
元素2
是 元素1
的 后代元素, CSS Selector 选择后代元素的语法是这样的元素1 元素2
中间是一个或者多个空格隔开
css 选择器支持通过任何属性来选择元素,语法是用一个方括号 []
。
举例
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据属性选择元素
element = wd.find_element(By.CSS_SELECTOR, '[href="http://www.miitbeian.gov.cn"]')
# 打印出元素对应的html
print(element.get_attribute('outerHTML'))
CSS 还可以选择 属性值 包含
某个字符串 的元素
比如, 要选择a节点,里面的href属性包含了 miitbeian 字符串,就可以这样写
a[href*="miitbeian"]
还可以 选择 属性值 以某个字符串 开头
的元素
比如, 要选择a节点,里面的href属性以 http 开头 ,就可以这样写
a[href^="http"]
还可以 选择 属性值 以某个字符串 结尾
的元素
比如, 要选择a节点,里面的href属性以 gov.cn 结尾 ,就可以这样写
a[href$="gov.cn"]
如果一个元素具有多个属性
<div class="misc" ctype="gun">沙漠之鹰div>
CSS 选择器 可以指定 选择的元素要 同时具有多个属性的限制,像这样 div[class=misc][ctype=gun]
比如, 我们要选择 网页 html 中的元素 版权
<div id='bottom'>
<div class='footer1'>
<span class='copyright'>版权span>
<span class='date'>发布日期:2018-03-03span>
div>
<div class='footer2'>
<span>备案号
<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号a>
span>
div>
div>
CSS selector 表达式 可以这样写:
div.footer1 > span.copyright
就是 选择 一个class 属性值为 copyright 的 span 节点, 并且要求其 必须是 class 属性值为 footer1 的 div节点 的子节点
也可以更简单:
.footer1 > .copyright
就是 选择 一个class 属性值为copyright 的节点(不限类型), 并且要求其 必须是 class 属性值为 footer1 的节点的 子节点
当然 这样也是可以的:
.footer1 .copyright
因为子元素同时也是后代元素
如果我们要 同时选择所有class 为 plant 和
class 为 animal 的元素。怎么办?
这种情况,css选择器可以 使用 逗号
,称之为 组选择 ,像这样
.plant , .animal
再看一个例子,比如选择所有 id 为 t1 里面的 span 和 p 元素
我们是不是应该这样写呢?
#t1 > span,p
不行哦,这样写的意思是 选择所有 id 为 t1 里面的 span
和 所有的 p 元素
只能这样写
#t1 > span , #t1 > p
我们可以指定选择的元素 是父元素的第几个子节点
使用 nth-child
如, 选择的是 第2个子元素,并且是span类型
所以这样可以这样写 span:nth-child(2)
,
如果你不加节点类型限制,直接这样写 :nth-child(2)
就是选择所有位置为第2个的所有元素,不管是什么类型
选择的是父元素的 倒数第几个子节点
,使用 nth-last-child
比如:
p:nth-last-child(1)
就是选择第倒数第1个子元素,并且是p元素、
我们可以指定选择的元素 是父元素的第几个 某类型的
子节点
使用 nth-of-type
<div id='t1'>
<h3> 唐诗 h3>
<span>李白span>
<p>静夜思p>
<span>杜甫span>
<p>春夜喜雨p>
div>
比如,
我们要选择 唐诗 和宋词 的第一个 作者,
可以像上面那样思考:选择的是 第2个子元素,并且是span类型
所以这样可以这样写 span:nth-child(2)
,
还可以这样思考,选择的是 第1个span类型
的子元素
所以也可以这样写 span:nth-of-type(1)
使用 nth-last-of-type
像这样
p:nth-last-of-type(2)
如果要选择的是父元素的 偶数节点
,使用 nth-child(even)
比如
p:nth-child(even)
如果要选择的是父元素的 奇数节点
,使用 nth-child(odd)
p:nth-child(odd)
如果要选择的是父元素的 某类型偶数节点
,使用 nth-of-type(even)
如果要选择的是父元素的 某类型奇数节点
,使用 nth-of-type(odd)
<h3> 唐诗 h3>
<span>李白span>
选择 h3 后面紧跟着的兄弟节点
span。
这就是一种 相邻兄弟 关系,可以这样写 h3 + span
表示元素 紧跟关系的 是 加号
如果要选择是 选择 h3 后面所有的兄弟节点
span,可以这样写 h3 ~ span
暂略
使用selenium自动化上传文件,我们只需要定位到该input元素,然后通过 send_keys 方法传入要上传的文件路径即可。
如下所示:
# 先定位到上传文件的 input 元素
ele = wd.find_element(By.CSS_SELECTOR, 'input[type=file]')
# 再调用 WebElement 对象的 send_keys 方法
ele.send_keys(r'h:\g02.png')
但是,有的网页上传,是没有 file 类型 的 input 元素的。
【Selenimu+AutoIT】非input标签上传文件(带参数)
从根节点开始的,到某个节点,每层都依次写下来,每层之间用 /
分隔的表达式,就是某元素的 绝对路径
/html/body/div
,就是一个绝对路径的xpath表达式, 等价于 css表达式 html>body>div
有的时候,我们需要选择网页中某个元素, 不管它在什么位置
。
xpath前面加 //
, 表示从当前节点往下寻找所有的后代元素,不管它在什么位置。
‘//’ 符号也可以继续加在后面,比如,要选择 所有的 div 元素里面的 所有的 p 元素 ,不管div 在什么位置,也不管p元素在div下面的什么位置,则可以这样写 //div//p
elements = driver.find_elements(By.XPATH, "//div//p")
如果使用CSS选择器,对应代码如下
elements = driver.find_elements(By.CSS_SELECTOR,"div p")
如果,要选择 所有的 div 元素里面的 直接子节点 p , xpath,就应该这样写了 //div/p
如果使用CSS选择器,则为 div > p
如果要选择所有div节点的所有直接子节点,可以使用表达式 //div/*
*
是一个通配符,对应任意节点名的元素,等价于CSS选择器 div > *
elements = driver.find_elements(By.XPATH, "//div/*")
根据属性来选择元素 是通过 这种格式来的 [@属性名='属性值']
注意:
如:
elements = driver.find_element(by=By.XPATH, value='//div[@role="textbox"]')