【爬虫】5.6 Selenium等待HTML元素

目录

任务目标

创建Ajax网站

创建服务器程序

[Selenium XX 等待](#Selenium XX 等待)

[1. Selenium强制等待](#1. Selenium强制等待)

[2. Selenium隐性等待](#2. Selenium隐性等待)

[3. Selenium循环等待](#3. Selenium循环等待)

[4. Selenium显示等待](#4. Selenium显示等待)

等待方法


任务目标

  • 在浏览器加载网页的过程中,网页的有些元素时常会有延迟的现象,在HTML元素还没有准备好的情况下去操作这个HTML元素必然会出现错误,这个时候Selenium需要等待HTML元素。例如:上节实例中出现的select的下拉框元素,选项填充需要执行JavaScript脚本。
  • 我们来学习如果使用Selenium等待延迟的HTML元素并最终爬取元素的数据。

创建Ajax网站

phone.html 如下:

注:phone.html 文件要位于 templates这个目录下

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form name="frm" action="/">
        <div>
            <span id="msg"></span>
            <label for="xmark"></label><select id="xmark"></select>
        </div>
        <input type="submit" value="提交" id="submit" disabled="true">
    </form>
</body>
<script>
    function loadMarks(){
        var http=new XMLHttpRequest(); 
        http.open("get","/marks",true);
        http.send(null);
        http.onreadystatechange=function(){
            // onreadystatechange存储函数,每当 readyState 属性改变时,就会触发调用该函数。
            // readystate存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
            // 0: 请求未初始化    1: 服务器连接已建立     2: 请求已接收    3: 请求处理中    4: 请求已完成,且响应已就绪
            // status,200(OK),404(未找到页面)
            if (http.readyState===4 && http.status===200){ //请求完成并且成功返回
                var xmark=document.getElementById("xmark"); 
                var xcolor=document.getElementById("xcolor"); 
                marks=eval("("+http.responseText+")");// JS中将JSON的字符串解析成JS对象格式
                for(var i=0;i<marks.length;i++) 
                    xmark.options.add(new Option(marks[i],marks[i])); 
                    document.getElementById("submit").disabled=false;
                    document.getElementById("msg").innerHTML="品牌";
            }
        };
    }
	loadMarks();
</script>
</html>

创建服务器程序

服务器server.py程序如下:

python 复制代码
import flask
import json
import time

app = flask.Flask(__name__)


@app.route("/")
def index():
    return flask.render_template("phone.html")


@app.route("/marks")
def loadMarks():
    time.sleep(1)
    marks = ["华为", "苹果", "三星"]
    return json.dumps(marks)  # 将JSON的对象格式转化成str格式


app.run()

模拟网站结果如下:


Selenium XX 等待

1. Selenium强制等待

必须等待的时间,缺点:不能准确把握需要等待的时间(有时操作还未完成,等待就结束了,导致报错;有时操作已经完成了,但等待时间还没有到,浪费时间),如果在用例中大量使用,会浪费不必要的等待时间,影响测试用例的执行效率。

python 复制代码
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("http://127.0.0.1:5000")

# 设置强制等待1.5秒,
time.sleep(1.5)

marks = driver.find_elements(By.XPATH, "//select/option")
print("品牌数量:", len(marks))
for mark in marks:
    print(mark.text)
form = driver.find_element(By.XPATH, "//form")
print(form.get_attribute("innerHTML").strip())
time.sleep(5)
driver.close()

2. Selenium隐性等待

该方法是浏览器对象调用的方法,即设置浏览器打开网页均等待的时长, 同样如果设置的隐性等待时间不够长, 还是爬取不到需要的数据。

python 复制代码
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()

# 设置隐性加载时间1.5秒,即网页在加载时最长等待 seconds 秒
driver.implicitly_wait(1.5)

driver.get("http://127.0.0.1:5000")
marks = driver.find_elements(By.XPATH, "//select/option")
print("品牌数量:", len(marks))
for mark in marks:
    print(mark.text)
form = driver.find_element(By.XPATH, "//form")
print(form.get_attribute("innerHTML").strip())
time.sleep(5)
driver.close()

3. Selenium循环等待

循环等待 实际上这个爬虫程序能否爬到数据的关键是<select>中是否已经出现了<option>元素,我们可以设置一个循环来判断是否有<option>元素

python 复制代码
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
try:
    driver.get("http://127.0.0.1:5000")
    waitTime = 0
    while waitTime < 10:
        marks = driver.find_elements(By.XPATH, "//select/option")
        if len(marks) > 0:
            break
        time.sleep(0.5)
        waitTime += 0.5
    if waitTime >= 10:
        raise Exception("Waiting time out")
    marks = driver.find_elements(By.XPATH, "//select/option")
    print("品牌数量:", len(marks))
    for mark in marks:
        print(mark.text)
        form = driver.find_element(By.XPATH, "//form")
        print(form.get_attribute("innerHTML").strip())
except Exception as err:
    print(err)
time.sleep(5)
driver.close()

循环等待实际上这个爬虫程序能否爬到数据的关键是<select>中是否已经出现了<option>元素,我们可以设置一个循环来判断是否有<option>元素。 这个程序中使用 waitTime 变量来构造一个循环,它最长等待 10 秒,每间隔 0.5 秒就检查一次<select>中是否有<option>存在,如果找到了<option>元素就退出等待循环,不然就继续等待直到<option>出现为止,如果 10 秒内还没有出现据抛出异常。

4. Selenium显示等待

python 复制代码
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
try:
    driver.get("http://127.0.0.1:5000")
    # 显示等待
    locator = (By.XPATH, "//select/option")
    WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located(locator))

    marks = driver.find_elements(By.XPATH, "//select/option")
    print("品牌数量:", len(marks))
    for mark in marks:
        print(mark.text)
        form = driver.find_element(By.XPATH, "//form")
        print(form.get_attribute("innerHTML").strip())
except Exception as err:
    print(err)

构造一个定位元素的 locator 的对象,例如通过 XPath 的方法定位<select>中的<option>元素:

locator=(By.XPATH,"//select/option")

使用 WebDriverWait 构造一个实例,调用 until 方法:

WebDriverWait(driver, 10,0.5).until(EC.presence_of_element_located(locator))

这条语句的含义是等待 locator 指定的元素出现,最长等待 10 秒,每间隔 0.5 秒就出现检查一次。如果在 10 秒内出现了该元素就是结束等待,否则就抛出一个异常,默认抛出异常为:NoSuchElementException。

这种等待的优点:等待判断准确,不会浪费多余的等待时间,在实际中使用可以提高执行效率。


等待方法

  1. EC.presence_of_element_located(locator)

这种形式是 等待 locator指定的元素 出现,也就是HTML文档中建立起了这个元素。
2. EC.visibility_of_element_located(locator)

这种形式是 等待 locator指定的元素 可见 ,注意元素出现时未见得可见,

例如:

<select id="xmark" style="display:none">...</select>

那么元素<select>是出现的但是不可见。
3. EC.element_to_be_clickable(locator)

这种形式是 等待 locator指定的元素 可以被点击

例如,在爬虫程序中等待 <input type="submit"> 按钮可用被点击:

locator = (By.XPATH, "//input[@type='submit']")

WebDriverWait(driver, 10,0.5).until(EC.element_to_be_clickable(locator))

或者等待 <option> 是否可以被点击: locator = (By.XPATH, "//select/option") WebDriverWait(driver,10,0.5).until(EC.element_to_be_clickable(locator))

使用这两种方法都可以爬取到手机品牌数据。

但是注意使用: locator = (By.XPATH, "//select") WebDriverWait(driver,10,0.5).until(EC.element_to_be_clickable(locator))

是等待<select>是否可以点击,这个元素就是没有<option>时也是可以点击的,因此用这个等待是爬取不到手机的品牌数据的。
4. EC.element_located_to_be_selected(locator)

这种形式是 等待 locator指定的元素 可以被选择,可以被选择的元素一般是<select>中的选项<option>、输入的多选框 <input type="checkbox"> 以及输入的单选框 <input type="radio">等元素。

locator = (By.XPATH, "//select/option")

WebDriverWait(driver, 10,0.5).until(EC.element_located_to_be_selected(locator))

同样能爬取到手机的品牌数据。

但是使用下列是不行的:

locator = (By.XPATH, "//input[@type='submit']")

WebDriverWait(driver, 10,0.5).until(EC.element_located_to_be_selected(locator))

因为这样的 <input type='submit'> 是怎么样也不可以选择的。
5. EC.text_to_be_present_in_element(locator,text)

这种形式是等待 locator 指定的元素的文本中包含指定的text文本,例如爬虫程序中使用下列的等待:

locator = (By.ID, "msg")

WebDriverWait(driver, 10,0.5).until(EC.text_to_be_present_in_element(locator,"品"))

即等待<span id="msg">......</span>元素中的文本包含"品"字,由于在<option>出现后设置文本是"品牌",因此爬虫程序可以爬取到手机品牌数据。


下一篇文章:实验项目一:【文本反爬网站的分析和爬取】

相关推荐
毕设源码-邱学长20 分钟前
【开题答辩全过程】以 基于Python的Bilibili平台数据分析与可视化实现为例,包含答辩的问题和答案
开发语言·python·数据分析
咚咚王者30 分钟前
人工智能之编程进阶 Python高级:第十一章 过渡项目
开发语言·人工智能·python
A尘埃1 小时前
大模型应用python+Java后端+Vue前端的整合
java·前端·python
A尘埃1 小时前
LLM大模型评估攻略
开发语言·python
一晌小贪欢2 小时前
【Python办公】处理 CSV和Excel 文件操作指南
开发语言·python·excel·excel操作·python办公·csv操作
檀越剑指大厂3 小时前
【Python系列】fastapi和flask中的阻塞问题
python·flask·fastapi
YoungHong19924 小时前
【Python进阶】告别繁琐Debug!Loguru一键输出异常日志与变量值
python·debug·异常处理·日志·loguru·log·logger
AiXed4 小时前
PC微信协议之nid算法
python·网络协议·算法·微信
小李哥哥5 小时前
基于数据的人工智能建模流程及源码示例
python
APIshop5 小时前
实战解析:苏宁易购 item_search 按关键字搜索商品API接口
开发语言·chrome·python