在这里插入图片描述

你好,我是安然无虞。


在这里插入图片描述

选择元素的基本方法

根据 id属性 选择元素

上一篇文章有讲解该方法, 请阅读: Python自动化测试·Selenium简单介绍

根据 class属性、tag名 选择元素

根据 class属性 选择元素

Web自动化的难点和重点之一, 就是如何 选择 我们想要操作的 Web页面元素.

除了根据元素的id值, 我们还可以根据元素的 class属性值 选择元素.

就像一个 学生张三 可以定义类型为 中国人或学生一样, 中国人和学生都是张三的类型.

元素也有类型, class属性就用来标志着元素类型.

我们点击这个教学网址

这个网址对应的HTML内容 有如下的部分:

<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>

所有的植物元素都有个class属性 值为 plant.

所有的动物元素都有个class属性 值为 animal.

如果我们要选择所有的动物, 就可以像下面这样写:

wd.find_elements(By.CLASS_NAME, 'animal')

注意这里是elements而不是element.

注意:

find_elements() 返回的是找到的符合条件的 所有元素 (这里有3个符合条件的元素), 放在一个列表中返回.

而如果我们使用 find_element() 方法, 就只会返回 第一个元素.

我们运行如下代码看看:

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

# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
wd = webdriver.Chrome()

# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
wd.get('https://www.byhy.net/cdn2/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)

执行结果如下:

狮子
老虎
山羊

首先我们要注意:

通过 WebElement 对象的 text属性 可以获取该元素 在网页中的文本内容.

所以下面的代码, 可以打印出 element 对应网页元素的文本.

print(element.text)

如果我们把上面的 find_elements() 方法换成 find_element() 方法:

element = wd.find_element(By.CLASS_NAME, 'animal')
print(element.text)

那么返回的就是第一个 class 属性值为 animal的元素, 也就是这个元素:

<div class="animal"><span>狮子</span></div>

就像一个学生张三可以定义有多个类型: 中国人和学生, 中国人 和 学生 都是张三的类型.

元素也可以有多个 class 类型, 多个class类型的值之间用 空格隔开, 比如:

<span class="chinese student">张三</span>

注意, 这里的span元素有两个class属性, 分别是chinese和student, 而不是一个名为 Chinese student 的属性.

我们要用代码选择这个元素, 可以指定任意一个 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')

根据 tag名 选择元素

类似的, 我们可以通过指定参数为 By.TAG_NAME, 选择所有的 tag名 为div 的元素, 如下所示:

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

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/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)

find_element 和 find_elements方法的区别

  • 使用 find_elements 方法选择的是符合条件的 所有元素, 并将结果存放到一个列表中返回, 如果没有符合条件的元素, 就返回空列表.

  • 使用 find_element 方法选择的是符合条件的 第一个元素, 如果没有符合条件的元素, 就会抛出 NoSuchElementException 异常.

通过 WebElement 对象选择元素

不仅 WebDriver 对象有 选择元素的方法, WebElement 对象也有 选择元素的方法.

WebElement对象 也可以调用 find_elements(), find_element()之类的方法.

  • WebDriver对象 选择元素的范围是 整个 web页面, 而 WebElement对象 选择元素的范围是 该元素的内部.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

wd = webdriver.Chrome(service=Service(r'/Users/sunlei/Desktop/chromedriver'))

wd.get('https://www.byhy.net/cdn2/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)

input()

执行结果如下:

内层11
内层12
内层21

等待界面元素出现

在我们进行网页操作的时候, 有的元素内容不是可以立即出现的, 可能会等待一段时间.

比如我们的股票搜索示例页面, 搜索一个股票名称, 我们点击搜索后, 浏览器需要把这个搜索请求发送给服务器进行处理后, 再把搜索结果返回给我们.

所以, 从点击搜索到得到结果, 需要一定的时间.

只是通常服务器处理速度比较快, 我们感觉好像立即得到了搜索结果.

搜索的每个结果对应的界面元素, 其 ID 分别是数字 1, 2, 3, 4 …

在这里插入图片描述
我们可以先用如下代码将第一个搜索结果里面的文本内容打印出来.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

wd = webdriver.Chrome(service=Service(r'/Users/sunlei/Desktop/chromedriver'))

wd.get('https://www.byhy.net/cdn2/files/selenium/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)

运行之后, 我们发现抛出异常了.

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="1"]"}

NoSuchElementException 的意思就是在当前的网页中没有找到该元素, 就是找不到 id 为 1 的元素.

这是为什么呢, 按道理来说是存在这个 id 为 1 的元素的.

其实是因为我们的代码执行的速度比网站响应的速度快.

网站还没有来得及返回搜索结果, 我们就执行了后续代码:

element = wd.find_element(By.ID, '1')

在那短暂的瞬间, 网页上是没有用 id 为 1 的元素的 (因为还没有搜索到结果). 这样的话程序有上面的报错是可以理解的.

那么怎么解决这个问题呢?

聪明的你可能会想到, 点击搜索后, 用 sleep 来等待几秒钟, 等百度服务器返回结果后, 再去选择 ID 为 1 的元素.

就像下面这样:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

wd = webdriver.Chrome(service=Service(r'/Users/sunlei/Desktop/chromedriver'))

wd.get('https://www.byhy.net/cdn2/files/selenium/stock1.html')

element = wd.find_element(By.ID, 'kw')

element.send_keys('通讯\n')

from time import sleep
sleep(1)

# 返回页面 ID为1 的元素
element = wd.find_element(By.ID, '1')
print(element.text)

运行之后是可以的, 没有之前的报错了.

但是按照上面的方式有个很大的问题, 就是: 设置等待多长时间合适呢?

这次百度网站反应可能比较快, 我们等待了1秒钟就可以了.

但是谁知道下次它的反应时间是不是还这么快呢? 毕竟曾经百度也出现过服务器瘫痪的事情.

可能有的老铁会说, 那我就等待久一点的时间, 等待20秒, 总归可以了吧.

这样也有一个很大的问题, 假如一个自动化程序里面需要10次等待, 就要花费200秒等待时间. 而大部分时间, 服务器的反应是很快的, 根本不需要等待20秒, 这样就造成了大量的时间浪费.

所以, 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
from selenium.webdriver.chrome.service import Service

wd = webdriver.Chrome(service=Service(r'/Users/sunlei/Desktop/chromedriver'))
wd.implicitly_wait(10)

wd.get('https://www.byhy.net/cdn2/files/selenium/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)

运行之后, 我们发现没有之前的错误了.

那么是不是有了 implicitly_wait(), 可以彻底不用 sleep()了呢?

不是的, 有的时候我们等待元素出现, 仍然需要sleep().

Selenium练习: 访问天气查询网站

题目: 访问天气查询网站(网址如下)

https://www.byhy.net/cdn2/files/selenium/jsweather.html

往下翻一页,会看到有 标题为

城市预报列表 (2021-08-09 18:00发布)

下面列出了江苏所有城市在某天的天气

请写代码,获取江苏所有城市的天气,并找出其中每天最低气温最低的城市,显示出来,比如 温度最低为12℃, 城市有连云港 盐城

代码如下:

# 请写代码,获取江苏所有城市的天气,并找出其中每天最低气温最低的城市,显示出来,比如 温度最低为12℃, 城市有连云港 盐城

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

wd = webdriver.Chrome(service=Service(r'/Users/sunlei/Desktop/chromedriver'))

wd.get(r'https://www.byhy.net/cdn2/files/selenium/jsweather.html')

element = wd.find_element(By.ID, 'forecastID')
print(element.text)


''' 
citysWeather是每个城市的温度信息 list

每个元素像这样:
南京
12℃/27
'''
citiesWeather = element.text.split('℃\n')

# 计算温度最低的城市
lowest = 70
lowestCity = [] # 温度最低城市列表
for one in citiesWeather:
    one = one.replace('℃', '')
    print(one)
    curcity = one.split('\n')[0]
    lowweather = one.split('/')[0].split('\n')[1]
    lowweather = int(lowweather)

    if lowweather < lowest:
        lowest = lowweather
        lowestCity = [curcity]
    elif lowweather == lowest:
        lowestCity.append(curcity)

print('温度最低为%s℃, 城市有%s' % (lowest, ' '.join(lowestCity)))

input()
遇见安然遇见你,不负代码不负卿。
谢谢老铁的时间,咱们下篇再见~
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐