资讯专栏INFORMATION COLUMN

爬虫 + 自动化利器 selenium 之自学成才篇(二)

AWang / 2735人阅读

摘要:耗时代码运行到这句之后触发隐式等待,在轮询检查后仍然没有定位到元素,抛出异常。耗时值得一提的是,对于定位不到元素的时候,从耗时方面隐式等待和强制等待没什么区别。

❤ 系列内容 ❤
爬虫+自动化利器 selenium 之自学成才篇(一)
主要内容:selenium 简介、selenium 安装、安装浏览器驱动、8 种方式定位页面元素、浏览器控制、鼠标控制、键盘控制

爬虫+自动化利器 selenium 之自学成才篇(二)
主要内容:三种等待方式(显式等待、隐式等待、强制等待)、一组元素的定位方式、切换操作(窗口切换、表单切换)、弹窗处理等。

爬虫+自动化利器 selenium 之自学成才篇(三)
主要内容:文件上传 & 下载、cookie 操作、调用 JavaScript(滑动滚动条)、关闭操作、页面截图等。



设置元素等待

很多页面都使用 ajax 技术,页面的元素不是同时被加载出来的,为了防止定位这些尚在加载的元素报错,可以设置元素等来增加脚本的稳定性。webdriver 中的等待分为 显式等待 和 隐式等待。

显式等待

显式等待:设置一个超时时间,每个一段时间就去检测一次该元素是否存在,如果存在则执行后续内容,如果超过最大时间(超时时间)则抛出超时异常(TimeoutException)。显示等待需要使用 WebDriverWait,同时配合 untilnot until 。下面详细讲解一下。

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)

  • driver:浏览器驱动
  • timeout:超时时间,单位秒
  • poll_frequency:每次检测的间隔时间,默认为0.5秒
  • ignored_exceptions:指定忽略的异常,如果在调用 untiluntil_not 的过程中抛出指定忽略的异常,则不中断代码,默认忽略的只有 NoSuchElementException

until(method, message=’ ‘)
until_not(method, message=’ ")

  • method:指定预期条件的判断方法,在等待期间,每隔一段时间调用该方法,判断元素是否存在,直到元素出现。until_not 正好相反,当元素消失或指定条件不成立,则继续执行后续代码
  • message: 如果超时,抛出 TimeoutException ,并显示 message 中的内容

method 中的预期条件判断方法是由 expected_conditions 提供,下面列举常用方法。

先定义一个定位器

from selenium.webdriver.common.by import Byfrom selenium import webdriverdriver = webdriver.Chrome()locator = (By.ID, "kw")element = driver.find_element_by_id("kw")
方法描述
title_is(‘百度一下’)判断当前页面的 title 是否等于预期
title_contains(‘百度’)判断当前页面的 title 是否包含预期字符串
presence_of_element_located(locator)判断元素是否被加到了 dom 树里,并不代表该元素一定可见
visibility_of_element_located(locator)判断元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0
visibility_of(element)跟上一个方法作用相同,但传入参数为 element
text_to_be_present_in_element(locator , ‘百度’)判断元素中的 text 是否包含了预期的字符串
text_to_be_present_in_element_value(locator , ‘某值’)判断元素中的 value 属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it(locator)判断该 frame 是否可以 switch 进去,True 则 switch 进去,反之 False
invisibility_of_element_located(locator)判断元素中是否不存在于 dom 树或不可见
element_to_be_clickable(locator)判断元素中是否可见并且是可点击的
staleness_of(element)等待元素从 dom 树中移除
element_to_be_selected(element)判断元素是否被选中,一般用在下拉列表
element_selection_state_to_be(element, True)判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False
element_located_selection_state_to_be(locator, True)跟上一个方法作用相同,但传入参数为 locator
alert_is_present()判断页面上是否存在 alert

下面写一个简单的例子,这里定位一个页面不存在的元素,抛出的异常信息正是我们指定的内容。

from selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Bydriver = webdriver.Chrome()element = WebDriverWait(driver, 5, 0.5).until(            EC.presence_of_element_located((By.ID, "kw")),                                           message="超时啦!")

隐式等待

隐式等待也是指定一个超时时间,如果超出这个时间指定元素还没有被加载出来,就会抛出 NoSuchElementException 异常。
除了抛出的异常不同外,还有一点,隐式等待是全局性的,即运行过程中,如果元素可以定位到,它不会影响代码运行,但如果定位不到,则它会以轮询的方式不断地访问元素直到元素被找到,若超过指定时间,则抛出异常

使用 implicitly_wait() 来实现隐式等待,使用难度相对于显式等待要简单很多。
示例:打开个人主页,设置一个隐式等待时间 5s,通过 id 定位一个不存在的元素,最后打印 抛出的异常 与 运行时间。

from selenium import webdriverfrom time import timedriver = webdriver.Chrome()driver.get("https://blog.csdn.net/qq_43965708")start = time()driver.implicitly_wait(5)try:    driver.find_element_by_id("kw")except Exception as e:    print(e)    print(f"耗时:{time()-start}")


代码运行到 driver.find_element_by_id("kw") 这句之后触发隐式等待,在轮询检查 5s 后仍然没有定位到元素,抛出异常。

强制等待

使用 time.sleep() 强制等待,设置固定的休眠时间,对于代码的运行效率会有影响。以上面的例子作为参照,将 隐式等待 改为 强制等待。

from selenium import webdriverfrom time import time, sleepdriver = webdriver.Chrome()driver.get("https://blog.csdn.net/qq_43965708")start = time()sleep(5)try:    driver.find_element_by_id("kw")except Exception as e:    print(e)    print(f"耗时:{time()-start}")


值得一提的是,对于定位不到元素的时候,从耗时方面隐式等待和强制等待没什么区别。但如果元素经过 2s 后被加载出来,这时隐式等待就会继续执行下面的代码,但 sleep还要继续等待 3s。


定位一组元素

上篇讲述了定位一个元素的 8 种方法,定位一组元素使用的方法只需要将 element 改为 elements 即可,它的使用场景一般是为了批量操作元素。

  • find_elements_by_id()
  • find_elements_by_name()
  • find_elements_by_class_name()
  • find_elements_by_tag_name()
  • find_elements_by_xpath()
  • find_elements_by_css_selector()
  • find_elements_by_link_text()
  • find_elements_by_partial_link_text()

这里以 CSDN 首页的一个 博客专家栏 为例。

下面使用 find_elements_by_xpath 来定位三位专家的名称。

这是专家名称部分的页面代码,不知各位有没有想到如何通过 xpath 定位这一组专家的名称呢?

from selenium import webdriver# 设置无头浏览器option = webdriver.ChromeOptions()option.add_argument("--headless")driver = webdriver.Chrome(options=option)driver.get("https://blog.csdn.net/")p_list = driver.find_elements_by_xpath("//p[@class="name"]")name = [p.text for p in p_list]name


切换操作

窗口切换

selenium 操作页面的时候,可能会因为点击某个链接而跳转到一个新的页面(打开了一个新标签页),这时候 selenium 实际还是处于上一个页面的,需要我们进行切换才能够定位最新页面上的元素。

窗口切换需要使用 switch_to.windows() 方法。

首先我们先看看下面的代码。

代码流程:先进入 【CSDN首页】,保存当前页面的句柄,然后再点击左侧 【CSDN官方博客】跳转进入新的标签页,再次保存页面的句柄,我们验证一下 selenium 会不会自动定位到新打开的窗口。

from selenium import webdriverhandles = []driver = webdriver.Chrome()driver.get("https://blog.csdn.net/")# 设置隐式等待driver.implicitly_wait(3)# 获取当前窗口的句柄handles.append(driver.current_window_handle)# 点击 python,进入分类页面driver.find_element_by_xpath("//*[@id="mainContent"]/aside/div[1]/div").click()# 获取当前窗口的句柄handles.append(driver.current_window_handle)print(handles)# 获取当前所有窗口的句柄print(driver.window_handles)


可以看到第一个列表 handle 是相同的,说明 selenium 实际操作的还是 CSDN首页 ,并未切换到新页面。
下面使用 switch_to.windows() 进行切换。

from selenium import webdriverhandles = []driver = webdriver.Chrome()driver.get("https://blog.csdn.net/")# 设置隐式等待driver.implicitly_wait(3)# 获取当前窗口的句柄handles.append(driver.current_window_handle)# 点击 python,进入分类页面driver.find_element_by_xpath("//*[@id="mainContent"]/aside/div[1]/div").click()# 切换窗口driver.switch_to.window(driver.window_handles[-1])# 获取当前窗口的句柄handles.append(driver.current_window_handle)print(handles)print(driver.window_handles)


上面代码在点击跳转后,使用 switch_to 切换窗口,window_handles 返回的 handle 列表是按照页面出现时间进行排序的,最新打开的页面肯定是最后一个,这样用 driver.window_handles[-1] + switch_to 即可跳转到最新打开的页面了。

那如果打开的窗口有多个,如何跳转到之前打开的窗口,如果确实有这个需求,那么打开窗口是就需要记录每一个窗口的 key(别名) 与 value(handle),保存到字典中,后续根据 key 来取 handle

表单切换

很多页面也会用带 frame/iframe 表单嵌套,对于这种内嵌的页面 selenium 是无法直接定位的,需要使用 switch_to.frame() 方法将当前操作的对象切换成 frame/iframe 内嵌的页面。

switch_to.frame() 默认可以用的 idname 属性直接定位,但如果 iframe 没有 idname ,这时就需要使用 xpath 进行定位。下面先写一个包含 iframe 的页面做测试用。

DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Documenttitle>    <style>        div p {            color: #red;            animation: change 2s infinite;        }        @keyframes change {            from {                color: red;            }            to {                color: blue;            }        }    style>head><body>    <div>        <p>公众号:Python新视野p>        <p>CSDN:Dream丶Killerp>        <p>微信:python-sunp>    div>    <iframe src="https://blog.csdn.net/qq_43965708" width="400" height="200">iframe>body>html>

现在我们定位红框中的 CSDN 按钮,可以跳转到 CSDN 首页。

from selenium import webdriverfrom pathlib import Pathdriver = webdriver.Chrome()# 读取本地html文件driver.get("file:///" + str(Path(Path.cwd(), "iframe测试.html")))# 1.通过id定位driver.switch_to.frame("CSDN_info")# 2.通过name定位# driver.switch_to.frame("Dream丶Killer")# 通过xpath定位# 3.iframe_label = driver.find_element_by_xpath("/html/body/iframe")# driver.switch_to.frame(iframe_label)driver.find_element_by_xpath("//*[@id="csdn-toolbar"]/div/div/div[1]/div/a/img").click()

这里列举了三种定位方式,都可以定位 iframe


弹窗处理

JavaScript 有三种弹窗 alert(确认)、confirm(确认、取消)、prompt(文本框、确认、取消)。

处理方式:先定位(switch_to.alert自动获取当前弹窗),再使用 textacceptdismisssend_keys 等方法进行操作

方法描述
text获取弹窗中的文字
accept接受(确认)弹窗内容
dismiss解除(取消)弹窗
send_keys发送文本至警告框

这里写一个简单的测试页面,其中包含三个按钮,分别对应三个弹窗。

DOCTYPE html><html lang="en"><head>head><body>    <button id="alert">alertbutton>    <button id="confirm">confirmbutton>    <button id="prompt">promptbutton>    <script type="text/javascript">        const dom1 = document.getElementById("alert")        dom1.addEventListener("click", function(){           alert("alert hello")        })                const dom2 = document.getElementById("confirm")        dom2.addEventListener("click", function(){            confirm("confirm hello")        })                const dom3 = document.getElementById("prompt")        dom3.addEventListener("click", function(){            prompt("prompt hello")        })    script>body>html>


下面使用上面的方法进行测试。为了防止弹窗操作过快,每次操作弹窗,都使用 sleep 强制等待一段时间。

from selenium import webdriverfrom pathlib import Pathfrom time import sleepdriver = webdriver.Firefox()driver.get("file:///" + str(Path(Path.cwd(), "弹窗.html")))sleep(2)# 点击alert按钮driver.find_element_by_xpath("//*[@id="alert"]").click()sleep(1)alert = driver.switch_to.alert# 打印alert弹窗的文本print(alert.text)# 确认alert.accept()sleep(2)# 点击confirm按钮driver.find_element_by_xpath("//*[@id="confirm"]").click()sleep(1)confirm = driver.switch_to.alertprint(confirm.text)# 取消confirm.dismiss()sleep(2)# 点击confirm按钮driver.find_element_by_xpath("//*[@id="prompt"]").click()sleep(1)prompt = driver.switch_to.alertprint(prompt.text)# 向prompt的输入框中传入文本prompt.send_keys("Dream丶Killer")sleep(2)prompt.accept()"""输出alert helloconfirm helloprompt hello"""

注:细心地读者应该会发现这次操作的浏览器是 Firefox ,为什么不用 Chrome 呢?原因是测试时发现执行 promptsend_keys 时,不能将文本填入输入框。尝试了各种方法并查看源码后确认不是代码的问题,之后通过其他渠道得知原因可能是 Chrome 的版本与 selenium 版本的问题,但也没有很方便的解决方案,因此没有继续深究,改用 Firefox 可成功运行。这里记录一下我的 Chrome 版本,如果有大佬懂得如何在 Chrome 上解决这个问题,请在评论区指导一下,提前感谢!
selenium:3.141.0
Chrome:94.0.4606.71

未完待续~


⭐️往期精彩,不容错过⭐️

总结篇
❤️两万字,50个pandas高频操作【图文并茂,值得收藏】❤️
❤️吐血总结《Mysql从入门到入魔》,图文并茂(建议收藏)❤️


工具篇
⭐️Python实用小工具之制作酷炫二维码(有界面、附源码)⭐️
❤️Python实用工具之制作证件照(有界面、附源码)❤️
❤️女朋友桌面文件杂乱无章?气得我用Python给她做了一个文件整理工具❤️

更多有趣的文章及干货,尽在

? ? ?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/122012.html

相关文章

  • ❤️爬虫+动化利器 selenium 自学成才(一)❤️

    文章目录 selenium 简介selenium安装安装浏览器驱动确定浏览器版本下载驱动 定位页面元素打开指定页面id 定位name 定位class 定位tag 定位xpath 定位css 定位link 定位partial_link 定位 浏览器控制修改浏览器窗口大小浏览器前进&后退浏览器刷新浏览器窗口切换常见操作 鼠标控制单击左键单击右键双击拖动鼠标悬停 键盘控制 seleni...

    james 评论0 收藏0
  • 零基础如何学爬虫技术

    摘要:楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,。本文来源知乎作者路人甲链接楚江数据提供网站数据采集和爬虫软件定制开发服务,服务范围涵盖社交网络电子商务分类信息学术研究等。 楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,http://www.chujiangdata.com。 第一:Python爬虫学习系列教程(来源于某博主:htt...

    KunMinX 评论0 收藏0
  • 首次公开,整理12年积累的博客收藏夹,零距离展示《收藏夹吃灰》系列博客

    摘要:时间永远都过得那么快,一晃从年注册,到现在已经过去了年那些被我藏在收藏夹吃灰的文章,已经太多了,是时候把他们整理一下了。那是因为收藏夹太乱,橡皮擦给设置私密了,不收拾不好看呀。 ...

    Harriet666 评论0 收藏0
  • 软件测试江湖()神兵利器

    摘要:软件测试江湖二神兵利器篇在上一篇文章中我们介绍了江湖上流传的各种软件测试的武功秘籍和心法,相信看过的小伙伴内力得到了很大的提升。功能测试篇功能测试,是软件测试里的入门级心法,自然也有与之相对应的兵器来发挥心法的最大功力。 软件测试江湖(二)神兵利器篇 在上一篇文章中我们介绍了江湖上流传的各种软件测试的武功秘籍和心法,相信看过的小伙伴内力得到了很大的提升。如果没有,一定是你看的姿势不对,...

    _Suqin 评论0 收藏0
  • 软件测试江湖()神兵利器

    摘要:软件测试江湖二神兵利器篇在上一篇文章中我们介绍了江湖上流传的各种软件测试的武功秘籍和心法,相信看过的小伙伴内力得到了很大的提升。功能测试篇功能测试,是软件测试里的入门级心法,自然也有与之相对应的兵器来发挥心法的最大功力。 软件测试江湖(二)神兵利器篇 在上一篇文章中我们介绍了江湖上流传的各种软件测试的武功秘籍和心法,相信看过的小伙伴内力得到了很大的提升。如果没有,一定是你看的姿势不对,...

    fai1017 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<