19、selenium

Source

Selenium 是一个web自动化工具

作用

  1. 自动化测试
    通过它,我们可以写出自动化程序,模拟浏览器里操作web界面。 比如点击界面按钮,在文本框中输入文字 等操作。
  2. 获取信息
    而且还能从web界面获取信息。 比如招聘网站职位信息,财经网站股票价格信息 等等,然后用程序进行分析处理。

运行环境

Selenium测试直接运行在浏览器中,就好像一个真正的用户在操作一样, 支持大部分主流的浏览器,包括IE(7,8,9,10,11),Firefox,Safari,Chrome,Opera等。
我们可以利用它来模拟用户点击访问网站,绕过一些复杂的认证场景
通过selnium+驱动浏览器这种组合可以直接渲染解析js,绕过大部分的参数构造和反爬。

注意事项

新版本的Selenium已经不在支持phantomjs,原作者也已经放弃维护该项目了。
还有在做爬虫的时候尽量不要用这种方法,Selenium+浏览器的组合速度慢,应付不了数据量比较大的爬取以及并发爬取。并且很吃电脑资源。

基本使用

原理


我们看到这里有一张原理图,那么selenium到底是怎么控制浏览器的呢,下面我就来给大家解释一下。

1. WebDriver API(基于Java、Python、C#等语言)

对于Python语言来说,就是下载下来的selenium库。

2. 浏览器的驱动(browser driver)

每个浏览器都有自己的驱动,均以exe文件形式存在

https://chromedriver.storage.googleapis.com/index.html

比如谷歌的chromedriver.exe、火狐的geckodriver.exe、IE的IEDriverServer.exe

3. 浏览器

浏览器当然就是我们很熟悉的常用的各种浏览器。

那在WebDriver脚本运行的时候,它们之间是如何通信的呢?为什么同一个browser driver即可以处理java语言的脚本,也可以处理python语言的脚本呢?

让我们来看一下,一条Selenium脚本执行时后端都发生了哪些事情:

  1. 对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动
  2. 浏览器驱动中包含了一个HTTP Server,用来接收这些http请求
  3. HTTP Server接收到请求后根据请求来具体操控对应的浏览器
  4. 浏览器执行具体的测试步骤
  5. 浏览器将步骤执行结果返回给HTTP Server
  6. HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息。

Created with Raphaël 2.2.0 自动化程序selenium库 浏览器 Chrome,Firefox 浏览器驱动,浏览器厂商提供

注意:每个驱动该对应每个浏览器;有时候浏览器会自动升级,导致浏览器不可用;

安装

selenium安装

终端输入 pip install selenium

浏览器驱动安装

chrome驱动下载地址:
https://chromedriver.storage.googleapis.com/index.html

元素选取

find_element(s)by_…方法

在一个页面中有很多不同的策略可以定位一个元素。我们可以选择最合适的方法去查找元素。Selenium提供了下列的方法:

注: 其中的element加上一个s,则是对应的多个元素的查找方法



单个元素查找方法 作用
find_element_by_xpath() 通过Xpath查找
find_element_by_class_name() 通过class属性查找
find_element_by_id() 通过id属性查找
find_element_by_name() 通过name属性进行查找
find_element_by_css_selector() 通过css选择器查找 语法规则
find_element_by_link_text() 通过链接文本查找
find_element_by_partial_link_text() 通过链接文本的部分匹配查找
find_element_by_tag_name() 通过标签名查找

By对象查找

除了以上的多种查找方式,还有两种私有方法集成了上面的所有的查找方法,让我们更方便的使用

方法 作用
find_element(By.XPATH, ‘//button/span’) 通过Xpath查找一个
find_elements(By.XPATH, ‘//button/span’) 通过Xpath查找多个

其中的第一个参数可以选择使用查找的方法,By.xxx 使用xxx方式解析,解析方法如下:

注:By对象导入: from selenium.webdriver.common.by import By

​ ID = “id”
​ XPATH = “xpath”
​ LINK_TEXT = “link text”
​ PARTIAL_LINK_TEXT = “partial link text”
​ NAME = “name”
​ TAG_NAME = “tag name”
​ CLASS_NAME = “class name”
​ CSS_SELECTOR = “css selector”


文本输入,提交


*接下来是文本的输入和提交,比如说我们需要通过selenium完成一个在网站中进行搜索的功能,前面我们已经知道了如何选取定位一个元素了

​ chrome.find_element_by_id(“all”).send_keys(“username”)

​ chrome.find_element_by_id(“password”).send_keys(“password”)

​ chrome.find_element_by_css_selector(“submit button”).click()

import time
from selenium  import webdriver
from  selenium.webdriver.common.by import By
wd=webdriver.Chrome()

wd.get("https://www.shiguangkey.com/course/list")


#元素选取
# input_tag1=wd.find_element_by_xpath('//input[@placeholder="搜索课程关键词"]')
# print(input_tag1)
# input_tag2=wd.find_elements_by_xpath('//input[@placeholder="搜索课程关键词"]')
# # print(input_tag2)
#写入文字
# input_tag1.send_keys("爬虫")

#通过xpath找到a标签
# a_tag=wd.find_element_by_xpath('//div[@class="_3AT4J"]/a')
# print(a_tag)
#使用链接文字获取标签
# a_tag=wd.find_element_by_link_text("实用外语")
# a_tag=wd.find_element_by_partial_link_text("搜索")
# print(a_tag.text)
# a_tag.click()

#通过class 属性获取标签
# div_tag1=wd.find_element_by_xpath('//div[@class="_3AT4J"]')
# print(div_tag1)
# div_tag2=wd.find_element_by_class_name("_3AT4J")
# print(div_tag2)

#通过id 获取标签  id="root"
# div_tag=wd.find_element_by_id("root")
# print(div_tag)

#通过name属性查找
# meta=wd.find_element_by_name("renderer")
# print(meta)

#css selector

# div=wd.find_element_by_css_selector("._3AT4J")
# print(div)
# div=wd.find_element_by_css_selector("#root")
# print(div)

#通过标签名字获取
# div=wd.find_element_by_tag_name("div")
# print(div)



#通用形式
input_tag=wd.find_element(By.XPATH,'//input[@placeholder="搜索课程关键词"]')
input_tag.send_keys("爬虫")
# a_tag=wd.find_element_by_xpath('//div[@class="_3AT4J"]/a')
a_tag=wd.find_element(By.XPATH,'//div[@class="_3AT4J"]/a')
a_tag.click()

动作切换

窗口切换

用selenium操作浏览器如果需要在打开新的页面,这个时候会有问题,因为我们用selenium操作的是第一个打开的窗口,所以新打开的页面我们是无法去操作的,所以我们要用到切换窗口:即handle切换的方法

方法 作用
js = 'window.open(“https://www.baidu.com”);'chrome.execute_script(js) 打开新标签
window_handles 获取所有页面窗口的句柄
current_window_handle 获取当前页面窗口的句柄
switch_to.window(window_name) 定位页面转到指定的window_name页面

Window_handles的顺序并不是浏览器上标签的顺序,尽量避免多标签操作


从这里的演示代码我们能看到,页面的handle是保存在一个列表中的,每个handle都是一个混乱的字符串,虽然能获取到当前标签的window_handle,但是很难知道其他页面对应的handle是什么,如果只有少数的标签也许还能应付,但是当打开了相当多标签时就很难对他们进行处理了,所以要尽量避免多标签的操作。


页面切换

在实际的爬虫中,明明定位的路径没问题,这个时候我们可以考虑一下是否是该页面存在frame的问题导有时候我们会遇到找不到元素的问题致的定位不到元素。


那么什么是frame呢,frame是一个框架标签,通过使用框架可以在一个浏览器窗口中显示不止一个页面,也就是说,在一个窗口中展示多个页面,每个页面称之为一个框架,并且每个框架独立于其他的框架。这个frame标签一共有三种,分别是frameset、frame、iframe,frameset跟其他普通标签没有区别,不会影响到正常的定位,而frame与iframe对selenium定位而言是一样的,selenium有一组方法对frame进行操作。reference是传入的参数,用来定位frame,可以传入id、name、index以及selenium的WebElement对象


方法 作用
switch_to.frame(frame_reference) 切到指定frame,可用id或name(str)、index(int)、元素(WebElement)定位
switch_to.parent_frame() 切到父级frame,如果已是主文档,则无效果, 相当于后退回去
switch_to_default_content() 切换到主页面,DOM树最开始的frame

页面弹窗

有的时候还会遇到弹窗的问题, 主要有两种一种是浏览器弹窗(alert/prompt),一种是自定义弹窗
自定义弹窗,就是一个自定义的div层,是隐藏页面中的,当触发了这个弹窗后,他就显示出来,这种方式我们通过正常的定位方式是可以定位到的。
alert弹窗,就要用下面的方法处理:

方法 作用
switch_to_alert 定位到alert弹窗,返回一个弹窗的对象
dismiss() 对弹窗对象的取消操作(相当于点击弹窗上的取消按钮)
accept() 对弹窗对象的确定操作(相当于点击弹窗上的确定按钮)
send_keys(key) 对弹窗对象内的输入框输入数据(针对于prompt弹窗)
text 获取弹窗内的文本

自定义的页面,用于演示弹窗切换

<html>
    <head>
        <meta charset="utf-8">
        <title>Alert</title>
    </head>
    <body>
        <input id = "alert" value = "alert" type = "button" onclick = "alert('welcome!请按确认继续!');"/>
        <input id = "confirm" value = "confirm" type = "button" onclick = "confirm('确定吗?');"/>
        <input id = "prompt" value = "prompt" type = "button" onclick = "var name = prompt('请输入你的名字:','请输入  你的名字'); document.write(name) "/>

    </body>
</html>

动作切换演示代码

from selenium import  webdriver
import time

wd=webdriver.Chrome()
# wd.get("https://www.shiguangkey.com/course/list")
# print(wd.title)
#二动作切换
#1.网页之间的切换

# db250js='window.open("https://movie.douban.com/top250")'
# wd.execute_script(db250js)
# print(wd.title)
# #切换窗口
# print(wd.window_handles)
# print(wd.current_window_handle)
# wd.switch_to.window(wd.window_handles[1])
# print(wd.title)


#2.iframe切换

# wd.get("https://study.163.com/")
# #点击掉同意
# span_tag=wd.find_element_by_xpath('//span[@class="ux-btn th-bk-main ux-btn- ux-btn- ux-modal-btn um-modal-btn_ok th-bk-main"]')
# span_tag.click()
# #id="j-pclose"  点击活动
# div_tag=wd.find_element_by_id("j-pclose")
# div_tag.click()
# login_tag=wd.find_element_by_xpath('//div[@class="go-login f-ib th-fs0fc8"]')
# login_tag.click()
#
# #可以直接写  id或name进行定位
# # wd.switch_to.frame('frame_id_name')
# time.sleep(3)#强制等待
# fr=wd.find_element_by_xpath('//iframe[@frameborder="0"]')
# print(fr)
# wd.switch_to.frame(fr)
# ph_tag=wd.find_element_by_id("phoneipt")
# ph_tag.send_keys("123456789")


#3.alert 切换   alert  confirm  prompt
# wd.get('http://localhost:63342/tzAdvanceSpider/7.selenium/alert_demo.html?_ijt=drere619rg6uro3ojl5pg0m8kf')
# time.sleep(1)
# wd.find_element_by_id("alert").click()
# time.sleep(1)
# alert_tag=wd.switch_to.alert
# alert_tag.accept()
# print(alert_tag.text)


# wd.get('http://localhost:63342/tzAdvanceSpider/7.selenium/alert_demo.html?_ijt=drere619rg6uro3ojl5pg0m8kf')
# time.sleep(1)
# wd.find_element_by_id("confirm").click()
# time.sleep(1)
# confirm_tag=wd.switch_to.alert
# print(confirm_tag.text)
# confirm_tag.accept()


wd.get('http://localhost:63342/python_spider/tzAdvanceSpider/7.selenium/alert_demo.html?_ijt=j4onpb6d0jam097dpk0q79t4rb')
time.sleep(1)
wd.find_element_by_id("prompt").click()
time.sleep(1)
prompt_tag=wd.switch_to.alert
print(prompt_tag.text)
prompt_tag.send_keys("爬虫大神")
time.sleep(1)
prompt_tag.accept()
time.sleep(1)

等待

简介

​ 在selenium操作浏览器的过程中,每一次请求url,selenium都会等待页面加载完成以后,才会将操作权限在交给我们的程序。
​ 但是,由于ajax和各种JS代码的异步加载问题,当一个页面被加载到浏览器时,该页面内的元素可以在不同的时间点被加载,这就使得元素的定位变得十分困难,当元素不再页面中时,使用selenium去查找的时候会抛出ElementNotVisibleException异常。
​ 为了解决这个问题,selenium提供了两种等待页面加载的方式,显示等待和隐式等待,让我们可以等待元素加载完成后在进行操作。

显式等待

显式等待: 显式等待指定某个条件,然后设置最长等待时间,程序每隔XX时间看一眼,如果条件成立,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出超时异常(TimeoutException)。
显示等待主要使用了WebDriverWait类与expected_conditions模块。
一般写法:WebDriverWait(driver, timeout, poll_frequency, igonred_exceptions).until(method, message)
Driver:传入WebDriver实例。
timeout: 超时时间,等待的最长时间(同时要考虑隐性等待时间)
poll_frequency: 调用until中的方法的间隔时间,默认是0.5秒
ignored_exceptions: 忽略的异常,如果在调用until的过程中抛出这个元组中的异常,则不中断代码,继续等待.
Method:可执行方法
Message: 超时时返回的信息

expected_conditions条件

expected_conditions是selenium的一个子模块,其中包含一系列可用于判断的条件,配合该类的方法,就能够根据条件而进行灵活地等待了

ActionChains提供的方法 作用
title_is title_contains 这两个条件类验证title,验证传入的参数title是否等于或包含于driver
presence_of_element_located presence_of_all_elements_located 这两个条件验证元素是否出现,传入的参数都是元组类型的locator,如(By.ID, ‘kw’)顾名思义,一个只要一个符合条件的元素加载出来就通过;另一个必须所有符合条件的元素都加载出来才行
visibility_of_element_located invisibility_of_element_located visibility_of 这三个条件验证元素是否可见,前两个传入参数是元组类型的locator,第三个传入WebElement
text_to_be_present_in_element text_to_be_present_in_element_value 判断某段文本是否出现在某元素中,一个判断元素的text,一个判断元素的value
frame_to_be_available_and_switch_to_it 判断frame是否可切入,可传入locator元组或者直接传入定位方式:id、name、index或WebElement
alert_is_present 判断是否有alert出现
element_to_be_clickable 判断元素是否可点击,传入locator

隐性等待

隐性等待implicitly_wait(xx) :设置了一个最长等待时间,如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间截止,然后执行下一步。
弊端就是程序会一直等待整个页面加载完成,就算你需要的元素加载出来了还是需要等待。,也就是一般情况下你看到浏览器标签栏那个小圈不再转,才会执行下一步,
隐性等待对整个driver的周期都起作用,所以只要设置一次即可
隐性等待和显性等待可以同时用,但要注意:等待的最长时间取两者之中的大者
默认等待时间为0,可以通过下面的方式设置:

from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) #隐式等待,最长10s
driver.get(‘https://www.baidu.com’)

强制等待

强制等待就是不论如何,在此处都需要阻塞等待一段时间,及time.sleep()

代码

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions  as  EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium import  webdriver
import time
a=time.time()
wd=webdriver.Chrome()
#3隐式等待
wd.implicitly_wait(10)
wd.get("https://study.163.com/")
#点击掉同意
span_tag=wd.find_element_by_xpath('//span[@class="ux-btn th-bk-main ux-btn- ux-btn- ux-modal-btn um-modal-btn_ok th-bk-main"]')
span_tag.click()
#id="j-pclose"  点击活动
div_tag=wd.find_elements_by_id("j-pclose")
if  div_tag:
    div_tag[0].click()
#跳转到登录页面
login_tag=wd.find_element_by_xpath('//div[@class="go-login f-ib th-fs0fc8"]')
login_tag.click()


#可以直接写  id或name进行定位
# wd.switch_to.frame('frame_id_name')
# time.sleep(3)#1强制等待

#2.显式等待
locater=(By.XPATH,'//iframe[@frameborder="0"]')
WebDriverWait(driver=wd,timeout=10, poll_frequency=0.5).until(EC.presence_of_element_located(locater),message="元素找不到")
fr=wd.find_element_by_xpath('//iframe[@frameborder="0"]')
print("frame",fr)
wd.switch_to.frame(fr)

time.sleep(3)#强制等待

#显示等待无作用
#locater2=(By.ID,"phoneipt")
#WebDriverWait(driver=wd,timeout=10, poll_frequency=0.5).until(EC.presence_of_element_located(locater2),message="手机号元素找不到")
#selenium.common.exceptions.NoSuchWindowException: Message: no such window
#这个显示等待会报错,没有这个窗口,是因为frame窗口还没有准备好 所以需要强制等待

ph_tag=wd.find_element_by_id("phoneipt")

ph_tag.send_keys("123456789")

动作链

在selenium当中除了简单的点击动作外,还有一些稍微复杂的动作,就需要用到ActionChains(动作链)这个子模块来满足我们的需求。

ActionChains可以完成复杂一点的页面交互行为,例如元素的拖拽,鼠标移动,悬停行为,内容菜单交互。
它的执行原理就是当调用ActionChains方法的时候不会立即执行,而是将所有的操作暂时储存在一个队列中,当调用perform()方法的时候,会按照队列中放入的先后顺序执行前面的操作。

ActionChains包:from selenium.webdriver.common.action_chains import ActionChains

方法

ActionChains提供的方法 作用
click(on_element=None) 鼠标左键单击传入的元素
double_click(on_element=None) 双击鼠标左键
context_click(on_element=None) 点击鼠标右键
click_and_hold(on_element=None) 点击鼠标左键,按住不放
release(on_element=None) 在某个元素位置松开鼠标左键
drag_and_drop(source, target) 拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset) 拖拽到某个坐标然后松开
move_to_element(to_element) 鼠标移动到某个元素
move_by_offset(xoffset, yoffset) 移动鼠标到指定的x,y位置
move_to_element_with_offset(to_element, xoffset, yoffset) 将鼠标移动到距某个元素多少距离的位置
perform() 执行链中的所有动作

演示网站

http://www.treejs.cn/v3/demo/cn/exedit/drag.html

from selenium import webdriver
from selenium.webdriver.common.action_chains  import ActionChains
import time

driver = webdriver.Chrome()
try:
    driver.get('http://www.treejs.cn/v3/demo/cn/exedit/drag.html')
    time.sleep(2)
    element = driver.find_element_by_id('treeDemo_2_span')
    target = driver.find_element_by_id('treeDemo_3_span')
    ActionChains(driver).drag_and_drop(element, target).perform()
    time.sleep(5)
finally:
    driver.quit()

其他

常用方法

方法 说明
Chrome.refresh() 刷新页面
Chrome.close() 关闭当前标签
Chrome.quit() 关闭所有标签
Chrome.page_source 网页源代码
Chrome.cookies 本页保存的cookie
Chrome.maximize_window() 最大化窗口

无界面设置

#设置chrome  无界面
from  selenium.webdriver.chrome.options import Options
from selenium import   webdriver
chrome_options=Options()
chrome_options.add_argument("--headless")
wd_headless=webdriver.Chrome(options=chrome_options)
wd_headless.get('http://www.treejs.cn/v3/demo/cn/exedit/drag.html')
print("无界面操纵",wd_headless.page_source)