目录
任务目标
创建Ajax网站
创建服务器程序
Selenium XX 等待
1. Selenium强制等待
2. Selenium隐性等待
3. Selenium循环等待
4. Selenium显示等待
等待方法
任务目标
- 在浏览器加载网页的过程中,网页的有些元素时常会有延迟的现象,在HTML元素还没有准备好的情况下去操作这个HTML元素必然会出现错误,这个时候Selenium需要等待HTML元素。例如:上节实例中出现的select的下拉框元素,选项填充需要执行JavaScript脚本。
- 我们来学习如果使用Selenium等待延迟的HTML元素并最终爬取元素的数据。
创建Ajax网站
phone.html 如下:
注:phone.html 文件要位于 templates 这个目录下
<!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程序如下:
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强制等待
必须等待的时间,缺点:不能准确把握需要等待的时间(有时操作还未完成,等待就结束了,导致报错;有时操作已经完成了,但等待时间还没有到,浪费时间),如果在用例中大量使用,会浪费不必要的等待时间,影响测试用例的执行效率。
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隐性等待
该方法是浏览器对象调用的方法,即设置浏览器打开网页均等待的时长, 同样如果设置的隐性等待时间不够长, 还是爬取不到需要的数据。
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>元素
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显示等待
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>出现后设置文本是"品牌",因此爬虫程序可以爬取到手机品牌数据。
下一篇文章:实验项目一:【文本反爬网站的分析和爬取】