一、页面等待
现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。为了解决这个问题。所以 Selenium 提供了两种等待方式:一种是隐式等待、一种是显式等待
为什么要等?
1 selenium比较慢 网站打开了 元素没有加载出来
2 有些数据是通过ajax加载的。加载需要时间
如何解决?
1 time.sleep() 靠谱 Python提供的
2 selenium也提供了页面等待的方式
- 隐式等待:调用driver.implicitly_wait。
- 显式等待:
显示等待是表明某个条件成立后才执行获取元素的操作。也可以在等待的时候指定一个最大的时间,如果超过这个时间那么就抛出一个异常。显示等待应该使用selenium.webdriver.support.excepted_conditions期望的条件和selenium.webdriver.support.ui.WebDriverWait来配合完成
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
# 隐式等待
# driver.get('https://www.baidu.com/')
# time.sleep(2) # 我们一定要等2秒吧
# driver.implicitly_wait(10) # 隐式等待 只要找到元素就立即执行
# driver.find_element_by_id('kwwww').send_keys('高合')
# 显示等待 gb_closeDefaultWarningWindowDialog_id
driver.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc')
driver.implicitly_wait(3)
# 千万不要忘记click() 这一步是去掉那个提示框
driver.find_element_by_id('gb_closeDefaultWarningWindowDialog_id').click()
# 等待出发地加载出来
WebDriverWait(driver,1000).until(
EC.text_to_be_present_in_element_value((By.ID,'fromStationText'),'北京')
)
# 等待目的地加载出啦
WebDriverWait(driver,1000).until(
EC.text_to_be_present_in_element_value((By.ID,'toStationText'),'长沙')
)
btn = driver.find_element_by_id('query_ticket')
# 按钮不能够被点击,注意这时应这么做
driver.execute_script('arguments[0].click()',btn)
总结
一些其他的等待条件
• presence_of_element_located:某个元素已经加载完毕了。
• presence_of_all_elements_located:网页中所有满足条件的元素都加载完毕了。
• element_to_be_clickable:某个元素是可以点击了。
更多条件请参考:http://selenium-python.readthedocs.io/waits.html
显式等待的模板
WebDriverWait(driver,1000).until(
EC.text_to_be_present_in_element_value((By.ID,'fromStationText'),'北京')
)
# 查询按钮不能够被点击
driver.execute_script('arguments[0].click()',btn)
解剖隐式等待与显式等待的区别:
隐式等待:
driver.implicitly_wait(10) # 隐式等待 只要找到元素就立即执行
driver.find_element_by_id('kw').send_keys('高合') #B
(翻译)wait until B(找到标签并输入值)且 最多找10秒,若over 10s 则报错
显式等待:
WebDriverWait(driver,10).until(
EC.text_to_be_present_in_element_value((By.ID,'fromStationText'),'北京') #A
)
driver.execute_script('arguments[0].click()',btn) #B
(翻译)wait until A (判断某个元素中的value属性是否包含了预期的字符串)且 最多找10秒,若over 10s 则报错,若是,则执行B
二、打开多窗口和切换页面
有时候窗口中有很多子tab页面。这时候肯定是需要进行切换的。selenium提供了一个叫做switch_to_window来进行切换,具体切换到哪个页面,可以从driver.window_handles中找到
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/') #1
# driver.get('https://www.douban.com/')
# 单纯的打开了另一个窗口
driver.execute_script('window.open("https://www.douban.com/")') #2
time.sleep(1)
# driver.close() #关闭的是第一个页面
# driver.find_element_by_id('kw').send_keys('brkalsy') #操作的是百度
# print(driver.current_url) # https://www.baidu.com/
driver.switch_to_window(driver.window_handles[1])
# driver.switch_to.window(driver.window_handles[1])
time.sleep(1)
print(driver.current_url)
三、案例(获取12306购票订单)
问题:获取12306购票订单
案例的学习目标 : 1 通过这个案例来自测一下目前自己的一个真实的水平 2 练习我们最近学习的知识点selenium 3 如何通过selenium抓取ajax数据
具体步骤(分析网页,选择合适的技术点)
1 登录 2 车次以及余票查询 3 解析车次列表 4 确认乘客信息及席别 5 核对信息
本次只码到登录成功
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
# 面向对象来实现
# 类是一个创建对象的对象 希望必须做一些事情 不做这个类就创建不成功
class TrainSpider(object):
login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录的url
personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 个人中心的url
def __init__(self,from_station,to_station,train_data):
self.from_station = from_station
self.to_station = to_station
self.train_data = train_data
def login(self):
driver.get(self.login_url)
# 登录也是一个耗时的操作 本次采用手机扫码登录
WebDriverWait(driver, 1000).until(
EC.url_contains(self.personal_url)
)
print('登录成功!')
# 封装了我们基本的功能
def run(self):
# 登录
self.login()
def main():
spider = TrainSpider('北京','长沙','2021-05-19')# 日期格式需要注意2021-05-19
spider.run()
if __name__ == '__main__':
main()
四、类知识点回顾
class A:
# 类属性 直接在类中定义的属性就是类属性
# 类属性可以通过类或类的实例来访问,无法通过实例对象来修改
count = 3
def __init__(self):
# 实例属性,通过实例对象添加的属性都是实例属性
# 实例属性 只能通过实例对象来访问和修改,类对象无法访问和修改
self.name = '葫芦娃'
# 实例方法
# 在类中定义,以self为第一个参数的方法都是实例方法
# 通过实例对象来调用,会自动将当期对象作为self传入 a.test()
# 当通过类对象来调用,不会自动传递self。此时我们必须手动传递self
def test(self):
print('我是实例方法....')
# 类方法
# 在类的内容部 使用@classmethod 来修饰的方法就是类方法
# 类方法的第一个参数是cls 也会被自动传递 cls就是当前的类对象
# 类方法可以通过类来调用也可以通过类的实例来调用,没有区别
@classmethod
def test2(cls):
print('我是类方法....')
a = A()
# a.count = 100 # 实例属性 注意:当使用实例对象对类方法进行赋值时,a这个实例对象也拥有的实例属性count
A.count = 1
print('A',A.count)
print('a',a.count)
# print('a',a.name)
# print('A',A.name) # AttributeError: type object 'A' has no attribute 'name'
# a.test() 等价于 A.test(a)
# a.test()
# A.test(a)
# A.test2() 等价于 a.test2()
# A.test2()
# a.test2()
技巧:记忆类属性、类方法、实例属性、实例方法:分别可以被谁调用与修改
五、补充小知识点
遇到 type=“hidden” 的标签:是不能send_keys 的
爬取数据(包括解析)
- selenium爬取数据
- selenium中的page_source+(三个方法)
- requests+(三个方法)
其中(三个方法 指正则、xpath、bs4)
输入信息的方式:
- send_keys类
- 下拉框
- 执行js
input标签的特殊所在:(无</>)