selenium常用元素定位、常用属性
使用技巧总结

selenium环境配置


参看之前的文章中介绍了selenium的环境安装

相关文档

selenium用法


声明浏览器对象

Selenium 支持非常多的浏览器,如 Chrome、Firefox、Edge 等

# 初始化
from selenium import webdriver
browser = webdriver.Chrome()  # 以下以chrome为例
# browser = webdriver.Firefox() 
# Ebrowser = webdriver.Edge() 
# browser = webdriver.Safari()
print(type(browser))
------输出结果---------
<class 'selenium.webdriver.chrome.webdriver.WebDriver'>

以下 browser 则表示是webdriver.Chrome(),是selenium.webdriver.chrome.webdriver.WebDriver类对象实例,查看源码发现该类继承自RemoteWebDriver

WebDriver常用方法和属性概览

  • get_network_conditions()
  • quit()、close()
  • get(url)
  • title、current_url、page_source、current_window_handle、window_handles
  • maximize_window()、fullscreen_window()、minimize_window()、set_window_size(width, height, windowHandle='current')、get_window_size(windowHandle='current')等
  • switch_to
# 应用示例
element = browser.switch_to.active_element
alert = browser.switch_to.alert
browser.switch_to.default_content()
browser.switch_to.frame('frame_name')
browser.switch_to.frame(1)
browser.switch_to.frame(browser.find_elements_by_tag_name("iframe")[0])
browser.switch_to.parent_frame()
browser.switch_to.window('main')
  • find_element(s)系列
  • execute_script(script, *args)、execute_async_script(script, *args)
  • switch_to_window(window_name)、switch_to_frame(frame_reference)、switch_to_alert()
  • back()、forward()、refresh()
  • get_cookies()、add_cookie(cookie_dict)等
  • implicitly_wait(time_to_wait)
  • get_screenshot_as_file(filename)、save_screenshot(filename)、get_screenshot_as_png()、get_screenshot_as_base64()

访问页面

  • browser.get(url)
from selenium import webdriver

# 驱动浏览器并获取网页源码
browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
print(browser.page_source)
browser.close()

定位元素

Selenium 提供了一系列定位元素的方法,我们可以用这些方法来获取想要的元素后,以便在自动化测试中操作并进行校验点验证或在爬虫中提取信息并存储

单个元素

  • find_element_by_id
    • 通过id属性定位元素
  • find_element_by_name
    • 通过name属性定位元素
  • find_element_by_xpath
    • 通过xpath表达式定位元素
    • 至于xpath表达式的规则可参看细说xpath
  • find_element_by_link_text
    • 通过超链接定位元素
  • find_element_by_partial_link_text
    • 通过部分(partial)超链接定位元素
  • find_element_by_tag_name
    • 通过tag_name属性定位元素
  • find_element_by_class_name
    • 通过class_name属性定位元素
  • find_element_by_css_selector
  • 找不到任何元素,会抛出 NoSuchElementException 异常
  • 如果找到有多个则返回页面的第一个元素,是WebElement类实例对象
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.hao123.com/')

# ele = browser.find_element_by_class_name('g-gc')   # 有多个,返回页面的第一个
# print(ele, type(ele))
# ele.click()

browser.find_element_by_xpath("//*[@data-hook='searchInput']").send_keys('123')
browser.find_element_by_css_selector("#search>form>[class='g-ib g-cp submitWrapper']").click()
  • 定位元素技巧
  1. 对于id, name, tag_name, class_name可在chrome开发者工具中-console中输入HTML DOM 方法来校验:
    • document.getElementById('xx_name'):返回对拥有指定 id 的第一个对象的引用
    • document.getElementsByName():返回带有指定名称的对象集合)
    • document.getElementsByTagName():返回带有指定标签名的对象集合
    • document.getElementsByClassName():所有指定类名的元素集合
  2. 对于xpath和css_selector可输入如下方法
    • $x('xpath expr') 或
    • $('css expr') 或 document.querySelectorAll():返回文档中匹配的CSS选择器的所有元素节点列表 或 document.querySelector():返回文档中匹配指定的CSS选择器的第一元素

进一步参看Document 对象属性和方法

多个元素

如果要查找所有满足条件的元素,需要用 find_elements_by_xxx 这样的方法(在这类方法的名称中,element 多了一个 s,注意区分)

find_elements_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
  • 用法同上
  • 返回list,元素都为WebElement类实例对象,如果仅找到一个元素,则list中仅一个元素
  • 找不到任何元素,不会抛出异常,返回list为空

注意:新版selenium中,find_element_by_xxfind_elements_by_xx将废弃,会有警告如:DeprecationWarning: find_elements_by_xpath is deprecated. Please use find_elements(by=By.XPATH, value=xpath) instead

find_element()和find_elements()

除上述的公共方法,Selenium 还提供了 find_element 和 find_element这两个通用方法,需要传入两个参数:查找方式 By 和对应的值。这种查找方式的功能和上面列举的查找函数完全一致,不过参数更加灵活。

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

browser = webdriver.Chrome()
browser.get('https://www.hao123.com/')

print(browser.find_element(By.CLASS_NAME, "g-gc"))  # 可等价表示为 browser.find_element_by_class_name('g-gc')
browser.close()

以下是By 类的一些可用属性(查看源码):

class By(object):
    """
    Set of supported locator strategies.
    """

    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"

WebElement类实例对象的属性和方法(元素操作)

元素被找到后,通过上面我们直到返回的都是WebElement类实例对象,Selenium 可以驱动浏览器来执行一些操作(让浏览器模拟执行一些动作)或获取一些元素的相关属性

元素操作

  • clear()
  • click()
  • send_keys(*value)
  • submit()
  • screenshot(filename)
  • get_attribute(name)
  • get_property(name)
  • is_displayed()
  • is_enabled()
  • is_selected()
  • value_of_css_property(property_name)
from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input = browser.find_element_by_id('q')
input.send_keys('iPhone')
time.sleep(1)
input.clear()
input.send_keys('iPad')
button = browser.find_element_by_class_name('btn-search')
button.click()

这里就不一一举例,具体查看API文档

相关属性

通过 webdriver的page_source 属性可以获取网页的源代码,接着就可以使用解析库(如正则表达式、Beautiful Soup、pyquery 等)来提取信息了,但 WebElement 类型元素有相关的方法和属性来直接提取元素信息,如属性、文本等。这样的话,我们就可以不用通过解析源代码来提取信息了,非常方便

  • id、location、location_once_scrolled_into_view、parent、rect、screenshot_as_base64、screenshot_as_png、size、tag_name、text
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://hao123.com')
hao123_recommend = browser.find_element_by_class_name('g-gc')
print(hao123_recommend)
if hao123_recommend.tag_name == 'a':
    print(hao123_recommend.get_attribute('href'))
print(hao123_recommend.text, hao123_recommend.id, hao123_recommend.location,
      hao123_recommend.size, hao123_recommend.parent, hao123_recommend.rect,
      hao123_recommend.location_once_scrolled_into_view)

browser.close()

切换Frame

网页中有一种节点叫作 iframe,也就是子 Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium 打开页面后,默认是在父级 Frame 里面操作,而此时如果页面中还有子 Frame,Selenium 是不能获取到子 Frame 里面的节点的。这时就需要使用 browser.switch_to.frame 方法来切换 Frame

ActionChains动作链

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')  # 切换到子frame
source = browser.find_element_by_css_selector('#draggable')  # 要拖拽的节点
target = browser.find_element_by_css_selector('#droppable')  # 目标节点
actions = ActionChains(browser)  # 声明 ActionChains 对象
actions.drag_and_drop(source, target)
actions.perform()  # perform方法执行动作

更多ActionChains api可参考官方api文档

执行js

  • 下拉进度条
    Selenium API 并没有提供实现某些操作的方法,比如,下拉进度条。但它可以直接模拟运行 JavaScript,此时使用 execute_script 方法即可实现
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')
browser.execute_script('window.open()') 

document.body.scrollHeight 将给出整个页面体的高度,可乘以一定的比例如·*1/2来完成滚动一定距离
window.open()新开一个选项卡

延时等待

在项目中应用时,由于网络原因或者某些页面有额外的 Ajax 请求,我们在网页源代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来
这里等待的方式有两种:一种是隐式等待,一种是显式等待

隐式等待

所谓隐式等待,即如果DOM 中没有找到元素则将继续等待,超出设定时间后,则抛出找不到节点的异常。换句话说,隐式等待可以在我们查找元素而元素没有立即出现的时候,等待一段时间再查找 DOM,默认的时间是 0

  • browser.implicitly_wait(time_to_wait)
from selenium import webdriver
browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('http://hao123.com')
browser.find_element_by_xpath("//*[@data-hook='searchInput']").send_keys('123')

显式等待

隐式等待的效果其实并没有那么好,因为我们只规定了一个固定时间,而页面的加载时间会受到网络条件的影响
还有一种更合适的显式等待方法,它指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常

  • WebDriverWait(driver, timeout).until(method, message='')
  • 部分源码
class WebDriverWait(object):
    def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
        """Constructor, takes a WebDriver instance and timeout in seconds.

           :Args:
            - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
            - timeout - Number of seconds before timing out
            - poll_frequency - sleep interval between calls
              By default, it is 0.5 second.
            - ignored_exceptions - iterable structure of exception classes ignored during calls.
              By default, it contains NoSuchElementException only.

           Example:
            from selenium.webdriver.support.ui import WebDriverWait \n
            element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
            is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
                        until_not(lambda x: x.find_element_by_id("someId").is_displayed())
        """
        ......
  • 示例
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

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser, 10)
input = wait.until(EC.presence_of_element_located((By.ID, 'q')))  # 在 10 秒内如果 ID 为 q 的节点(即搜索框)成功加载出来,就返回该节点,否则就会抛出TimeoutException异常
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search'))) # 对与按钮10 秒内如果可点击的,也就代表它成功加载出来了,就会返回这个按钮节点,否则就会抛出TimeoutException异常
print(input, button)
  • until中method参数常用selenium.webdriver.support.expected_conditions包中的方法,如下为常用的方法,具体使用可参看源码或API文档
等待条件 含义
title_is 标题是某内容
title_contains 标题包含某内容
presence_of_element_located 元素可定位(加载出),传入locate元组如(By.ID, 'p')
visibility_of_element_located 元素可见,传入locate元组
visibility_of 元素可见,传入元素对象
presence_of_all_elements_located 所有元素可定位
visibility_of_all_elements_located 所有元素可见
text_to_be_present_in_element 某个元素文本包含某文字
text_to_be_present_in_element_value 某个元素值包含某文字
frame_to_be_available_and_switch_to_it 加载并切换
invisibility_of_element_located 元素不可定位
invisibility_of_element 元素不可见
element_to_be_clickable 元素可点击
element_to_be_selected 元素可选择,传入元素对象
element_located_to_be_selected 元素可选择,传入locate元组
element_selection_state_to_be 传入元素对象以及状态,相等返回True,否则False
element_located_selection_state_to_be 传入locate元组以及状态,相等返回True,否则False
alert_is_present 是否出现alert

前进后退

平常我们使用浏览器时都有前进、后退和刷新功能,Selenium 也可以完成这个操作

  • back()、forward()、refresh()
import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com/')
browser.get('https://www.zhihu.com/')
browser.get('https://www.hao123.com/')
browser.back()
time.sleep(1)
browser.forward()
browser.close()

选项卡管理

访问网页的时候,我们通常会开启多个选项卡

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')  # 新开一个选项卡
print(browser.window_handles)  # 获取window_handles
browser.switch_to.window(browser.window_handles[1])
browser.get('https://www.zhihu.com')
time.sleep(1)
browser.switch_to.window(browser.window_handles[0])
browser.get('http://hao123.com')

Cookies

可以方便地对 Cookies 进行操作,例如获取、添加、删除 Cookies

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'jerry'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())

异常处理

可以使用 try except 语句来捕获各种异常,如上NoSuchElementException,TimeoutException等

无头模式


Chrome 浏览器从 60 版本已经支持了无头模式,即 Headless。无头模式在运行的时候不会再弹出浏览器窗口,减少了干扰,而且它减少了一些资源的加载,如图片等资源,所以也在一定程度上节省了资源加载时间和网络带宽

  • ChromeOptions 来开启 Chrome Headless 模式
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument('--headless')
browser = webdriver.Chrome(options=option)
browser.set_window_size(1366, 768)
browser.get('https://www.baidu.com')
browser.get_screenshot_as_file('preview.png')

反屏蔽


现在很多网站都加上了对 Selenium 的检测,来防止一些爬虫的恶意爬取,至于反屏蔽的手段也不在本篇介绍范围内,可进一步关注后续文章

更多


appium的应用其实跟selenium很相似,可对比参看appium应用基于Page Object的测试框架介绍

selenium-python-helium


Helium 针对 Selenium 进行了封装,屏蔽了 Selenium 很多实现细节,提供了更加简洁直观的 API,更方便进行 Web 端的自动化,目前仅支持 Chrome 和 FireFox

Helium我也没用过,这里再贴几篇参考文章: