Web自动化 - 选择操作元素 1

所有的 UI (用户界面)操作 的自动化,都需要选择界面元素。

选择界面元素就是:先让程序能找到你要操作的界面元素。

先找到元素,才能操作元素。


选择元素的方法

程序 怎么才能找到 要操作的 web 界面元素?

方法就是要根据这个 web 元素的 特征 去选择。

元素的特征怎么查看?

可以使用浏览器的开发者工具栏帮我们查看、选择web 元素。

请大家安装最新版的Chrome浏览器(可以百度搜索下载)。

用chrome浏览器访问百度,按F12后,点击下图箭头处的Elements标签,即可查看页面对应的HTML 元素

default

然后,再点击 最左边的图标,如下所示

default

之后,鼠标在界面上点击哪个元素,就可以查看该元素对应的html标签内容了。

比如,前面的图的高亮处,就是百度搜索输入框对应的input元素。

根据 元素的id 属性选择元素

大家仔细看上面的 input元素 内容,会发现它有一个属性叫id。

default

我们可以把 id 想象成元素的编号, 是用来在html中标记该元素的。 根据规范, 如果元素有id ,这个id 必须是当前html中唯一的。

所以如果元素有id, 根据id选择元素是最简单高效的方式。

下面的代码,就是用selenium 访问百度,并且在输入框中搜索 黑羽魔巫宗

大家可以运行一下看看。

from selenium import webdriver

# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
driver.get('https://www.baidu.com')

# 根据id选择元素,返回的就是该元素对应的WebElement对象
element = driver.find_element_by_id('kw')

# 通过该 WebElement对象,就可以对页面元素进行操作了
# 比如输入字符串到 这个 输入框里
element.send_keys('黑羽魔巫宗\n')

其中

driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

返回的是 WebDriver 实例对象,我们可以通过这个实例对象来操控浏览器,比如 打开网址、选择界面元素等。

下面的代码

driver.find_element_by_id('kw')

就是使用 WebDriver 实例对象 的方法find_element_by_id, 这个方法就是 根据输入框元素的 id 值 ‘kw’ 来选择到元素。

选择到元素之后, find_element_by_id 方法会返回一个 WebElement, 我们通过这个对象就可以操控对应的界面元素。

比如 send_keys 方法就是在对应的元素中输入字符串,

而 click 方法就可以点击该元素。


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

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

大家请访问这个网址 http://www.python3.vip/doc/tutorial/python/code/sample1.html

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

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>白月黑羽测试网页1</title>

        <style>
            .animal {color: red;}
        </style>
    </head>

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

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

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

如果我们要选择所有的 动物, 就可以使用方法 find_elements_by_class_name

driver.find_elements_by_class_name('animal')

注意

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

而如果我们使用 find_element_by_class_name (注意少了一个s) 方法, 就只会返回第一个元素。

大家可以运行如下代码看看。

from selenium import webdriver

# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
driver.get('http://www.python3.vip/doc/tutorial/python/code/sample1.html')

# 根据 class name 选择元素,返回的是 一个列表
# 里面 都是class 属性值为 animal的元素对应的 WebElement对象
elements = driver.find_elements_by_class_name('animal')

# 取出列表中的每个 WebElement对象,打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
    print(element.text)


如果我们把

elements = driver.find_elements_by_class_name('animal')

去掉一个s ,改为

element = driver.find_element_by_class_name('animal')
print(element.text)

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

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




类似的,我们可以通过方法 find_elements_by_tag_name ,选择所有的tag名为 div的元素,如下所示

from selenium import webdriver

# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
driver.get('http://www.python3.vip/doc/tutorial/python/code/sample1.html')

# 根据 tag name 选择元素,返回的是 一个列表
# 里面 都是 tag 名为 div 的元素对应的 WebElement对象
elements = driver.find_elements_by_tag_name('div')

# 取出列表中的每个 WebElement对象,打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
    print(element.text)

等待界面元素出现

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

比如 百度搜索一个词语, 我们点击搜索后, 浏览器需要把这个搜索请求发送给百度服务器, 百度服务器进行处理后,把搜索结果返回给我们。

只是通常百度服务器的处理比较快,我们感觉好像是立即出现了搜索结果。

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

如下

default

那么我们可以试试用如下代码 来将 第一个搜索结果里面的文本内容 打印出来

from selenium import webdriver

driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

driver.get('https://www.baidu.com')

element = driver.find_element_by_id('kw')

element.send_keys('黑羽魔巫宗\n')

# id 为 1 的元素 就是第一个搜索结果
element = driver.find_element_by_id('1')

# 打印出 第一个搜索结果的文本字符串
print (element.text)

如果大家去运行一下,就会发现有如下异常抛出

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

NoSuchElementException 的意思就是在当前的网页上 找不到该元素, 就是找不到 id 为 1 的元素。

为什么呢?

因为我们的代码执行的速度比 百度服务器响应的速度 快。

百度还没有来得及 返回搜索结果,我们就执行了如下代码

element = driver.find_element_by_id('1')

在那短暂的瞬间, 网页上是没有用 id为1的元素的 (因为还没有搜索结果呢)。自然就会报告错误 id为1 的元素不存在了。

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

很多聪明的读者可以想到, 点击搜索后, 用sleep 来 等待几秒钟, 等百度服务器返回结果后,再去选择 id 为1 的元素, 就像下面这样

from selenium import webdriver

driver = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

driver.get('https://www.baidu.com')

element = driver.find_element_by_id('kw')

element.send_keys('黑羽魔巫宗\n')

# 等待 2 秒
from time import sleep
sleep(2)

# 2 秒 过后,再去搜索
element = driver.find_element_by_id('1')

# 打印出 第一个搜索结果的文本字符串
print (element.text)

大家可以运行一下,基本是可以的,不会再报错了。

但是这样的方法 有个很大的问题,就是设置等待多长时间合适呢?

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

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

可能有的读者说,我干脆sleep比较长的时间, 等待 20 秒, 总归可以了吧?

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


Selenium提供了一个更合理的解决方案,是这样的:

当发现元素没有找到的时候, 并不 立即抛出 找不到元素的异常。

而是周期性(每隔半秒钟)重新寻找该元素,直到该元素找到,

或者超出指定最大等待时长,这时才 抛出异常(如果是 find_elements 则是返回空列表)。

Selenium 的 Webdriver 对象 有个方法叫 implicitly_wait

该方法接受一个参数 就是指定的 最大等待时长。

如果我们 加入如下代码

driver.implicitly_wait(10)

那么后续所有的 find_element 或者 find_elements 之类的方法调用 都会采用上面的策略。

就是找不到元素 每隔 半秒钟 再去界面上查看一次, 直到找到该元素, 或者 过了10秒钟 最大时长。

这样,我们的百度搜索的例子的最终代码如下

from selenium import webdriver

driver = webdriver.Chrome()

# 设置最大等待时长为 10秒
driver.implicitly_wait(10)

driver.get('https://www.baidu.com')

element = driver.find_element_by_id('kw')

element.send_keys('黑羽魔巫宗\n')

element = driver.find_element_by_id('1')

print (element.text)

大家再运行一下,可以发现不会有错误了。