天天看点

Selenium的使用

昨天搞了一天搞这个谷歌驱动,还是没成功到最后还是选择了用狐火来进行selenium的使用首先我们使用的环境是FireFox47和python3.6,驱动地址链接:https://pan.baidu.com/s/1THxUan49YlKOwSTpFpshWg 密码:ibcw下载好后放置在python的script的目录下,不知道怎么回事我用谷歌浏览器一直就是使用不起来,昨天搞了一天了,百度说是驱动问题,但我驱动和浏览器的版本都是对应的,不存在驱动问题的。

言归正传,Selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。

一.声明浏览器对象

Selenium支持非常多的浏览器,如Chrome、FireFox、Edge等我们用如下方式进行初始化:

from selenium import webdriver
browser=webdriver.Chrome()
browser=webdriver.Firefox()
browser=webdriver.Edge()           

这样就完成了浏览器对象的初始化并将其赋值为browser对象。接下来,我们要做的就是调用browser对象,让其执行各个动作以模拟浏览器操作。

二.访问页面

我们可以用get()方法请求网页,参数传入链接URL即可。如下所示:

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
print(browser.page_source)   #打印输出网页源代码
browser.close()           

通过这几行简单的代码,我们可以实现浏览器的驱动并获取网页源码,非常便捷。

三.查找节点

Selenium可以驱动浏览器完成各种操作,比如填充表单、模拟点击等。比如,我们想要完成向某个输入框输入文字操作,总需要知道这个输入框的位置,而selenium提供一系列查找节点的方法

单个节点

我们可以用find_element_by_name()根据那么值获取,find_element_by_id()是根据id获取。另外,还有根据XPath、CSS选择器等获取方式。

如下所示:

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
input_first=browser.find_element_by_id('q')
input_second=browser.find_element_by_css_selector('#q')
input_third=browser.find_element_by_xpath('//*[@id="q"]')
print(input_first,input_second,input_third)
browser.close()           

这里我们使用3种方式获取输入框,分别是ID,CSS选择器和XPath获取,它们返回的结果完全一致。运行结果如下:

<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")>
           

可以看到,这3个节点都是WebElement类型,是完全一致的。

这里列出所有获取单个节点的方法:

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector           

 另外,Selenium还提供了通用方法find_element(),它需要传入两个参数:查找方式By和值。实际上,它就是find_element_by_id()这种方法的通用函数版本,比如find_element_by_id(id)就等价于find_element(By.ID,id),二者得到的结果完全一致。我们用代码实现下:

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input_first = browser.find_element(By.ID, 'q')
print(input_first)
browser.close()           

多个节点

如果查找的目标在网页上只有一个,那么完全可以用find_element()方法。但如果有多个节点,再用find_element()方法查找,就只能得到第一个节点了。如果要查找所有满足条件的节点,需要用find_elements()这样的方法。如下所示:

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
lis=browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()           

运行结果如下:

from selenium import webdriver
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
input=browser.find_element_by_class_name('zu-top-add-question')
print(input.text)           
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")>           

可以看到,得到的内容变成了列表类型,列表中的每个节点都是WebElement类型。

也就是说,如果我们用find_element()方法,只能获取匹配的第一个节点,结点是WebElement类型。如果用find_elements()方法,则结果是列表类型,列表中的每个节点是WebElement类型。

这里列出所有获取多个节点的方法:

find_elementS_by_id
find_elementS_by_name
find_elementS_by_xpath
find_element_by_link_text
find_elementS_by_partial_link_text
find_elementS_by_tag_name
find_elementS_by_class_name
find_elementS_by_css_selector           

当然,我们也可以直接用find_elements()方法来选择,这时可以这样写:

lis=browser.find_elements(By.CSS_SELECTOR,'.service-bd li')           

结果是完全一致的。

节点交互

Selenium可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。比较常见的用法有:输入文字时用send_keys()方法,清空文字用clear()方法,点击按钮时用click()方法,示例如下:

from selenium import webdriver
import time
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
input=browser.find_element_by_id('q')
input.send_keys('iPhone')
time.sleep(1)
input.clear()
input.send_keys('iPad')
button=browser.find_element_by_class_name('btn-search')
button.click()           

这里首先驱动浏览器打开淘宝,然后用find_element_by_id()方法获取输入框,然后用send_keys()方法输入iPhone文字,等待一秒后用clear()方法清空输入框,再次调用send_keys()方法输入iPad文字,之后再用find_element_by_class_name()方法获取搜索按钮,最后调用click()方法完成搜索动作。

动作链

在上面的实例中,一些交互动作都是针对某个节点执行的。比如,对于输入框,我们就调用它的输入文字和清空文字方法;对于按钮,就调用它的点击方法。其实,还有另外一些操作,它们没有特定的执行对象,这些动作用另一种方式来执行,那就是动作链。

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Firefox()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element_by_css_selector('#draggable')
target = browser.find_element_by_css_selector('#droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source, target)
actions.perform()           

首先我们打开网页中的一个拖拽实例,然后依次选中要被拖拽的节点和拖拽到的目标节点,然后声明了 ActionChains 对象赋值为 actions 变量,然后通过调用 actions 变量的 drag_and_drop() 方法,然后再调用 perform() 方法执行动作,就完成了拖拽操作.

执行JavaScript

我们用execut_script()方法实现,如下所示:

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')           

这里我们利用excute_script()方法将进度条下拉到最底部,然后弹出alert提示框

获取节点信息

selenium提供了选择节点的方法,返回的是WebElement类型,那么它也有相关的方法和属性来直接提取节点信息,如文本、属性等。这样的话,我们就可以不通过解析源代码来提取信息。

  • 获取属性

我们可以使用get_attribute()方法来获取节点的属性,但是前提是先选中这个节点,示例如下:

from selenium import webdriver
from selenium.webdriver import ActionChains
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
logo=browser.find_element_by_id('zh-top-link-logo')
print(logo)
print(logo.get_attribute('class'))
           

运行之后,程序便会用火狐浏览器打开知乎页面,然后获取知乎的logo节点,最后打印出它的class。

输出结果如下

<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="c3095667-b9d9-4100-8538-047d3d97a247", element="f286250c-48eb-4189-bf7b-d993e3339ce0")>
zu-top-link-logo           

通过get_attribute()方法,然后传入想要获取的属性名,就可以得到它的值了。

  • 获取文本值

每个WebElement节点都有text属性,直接调用这个属性就可以得到节点内部的文本信息,这相当于Beautiful Soup的get_text()方法,如下所示:

from selenium import webdriver
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
input=browser.find_element_by_class_name('zu-top-add-question')
print(input.text)           

这里获取的“提问”这个节点,再将其文本值打印出来。

控制台输出结果如下:

提问
           
  • 获取id、位置、标签名和大小

另外,WebElement节点还有其他一些属性,比如id属性可以获取id,location属性可以获取该节点在页面中的相对位置,tag_name属性可以获取标签名称,size属性可以获取节点的大小,也就是宽高,如下所示:

from selenium import webdriver

browser = webdriver.Firefox()
url = 'https://www.zhihu.com/explore'
browser.get(url)
input = browser.find_element_by_class_name('zu-top-add-question')
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)           

这里首先获得"提问“按钮这个节点,然后调用其id、location、tag_name、size属性来获取对应的属性值。

切换Frame

我们知道网页中有一种节点叫做iframe,也就是子Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium打开网页后,它默认是在父级节点Frame里面操作,而此时如果页面中还有子Frame,它是不能获取到子Frame里面的节点的。这时需要使用switch_to,.frame()方法来切换Frame。示例如下:

import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
browser=webdriver.Firefox()
url='http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
try:
    logo=browser.find_element_by_class_name('logo')
except NoSuchElementException:
    print('NO LOGO')
browser.switch_to.parent_frame()
logo=browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)           

输出如下:

NO LOGO
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="f8e8a398-fb94-4dfd-89b6-4fe3bfb50c52", element="29b03253-8b92-4187-bb44-388aecb60f45")>
RUNOOB.COM
           

这里首先通过switc_to.frame()方法切换到子Frame里面,然后尝试获取父级Frame里的logo节点(这里是不可能找到的),如果找不到的话,就会抛出NoSuchElementException异常,异常被捕捉之后,就会输出NO LOGO。接下来,重新切换到父级Frame,然后再次重新获取节点,发现此时可以成功获取了。

switch_to.frame()方法是当页面中包含子Frame时,获取子Frame中的节点。

延时等待

  • 隐式等待

当使用隐式等待执行测试的时候,如果Selenium没有在DOM中找到节点,将继续等待,超出设定时间后,则抛出找不到节点的异常。如下所示:

from selenium import webdriver

browser = webdriver.Firefox()
browser.implicitly_wait(10)
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)           
  • 显示等待

显示等待它指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来这个节点,就返回查找的节点;如果到了规定的时间依然没有加载出该节点,则抛出异常,如下所示:

from selenium import webdriver
from selenium.webdriver.common.by import  By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser=webdriver.Firefox()
browser.get('https://www.taobao.com/')
wait=WebDriverWait(browser,10)
input=wait.until(EC.presence_of_element_located((By.ID,'q')))
button=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))
print(input,button)           

这里首先引入WebDriverWait这个对象,指定最长等待时间,然后调用它的until()方法,传入要等待条件expected_condititions。比如,这里传入了presence_of_element_located这个条件,代表节点出现的意思。其参数是节点的定位元祖,也就是ID为q的节点搜索框。

这样可以做到的效果就是,在10秒内如果ID为q的节点成功加载出来,就返回该节点;如果超过10秒还没加载出来,就抛出异常

对于按钮,可以更改一下等待条件,比如改为 element_to_be_clickable,也就是可点击,所以查找按钮时是查找 CSS 选择器为 .btn-search 的按钮,如果 10 秒内它是可点击的也就是成功加载出来了,那就返回这个按钮节点,如果超过 10 秒还不可点击,也就是没有加载出来,那就抛出异常。

运行代码,在网速较佳的情况下是可以成功加载出来的。

控制台输出:

<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="a9445dae-1106-4c33-9f88-7aa757587287", element="b539ab53-bbbc-4018-8e75-9eb179b28b80")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="a9445dae-1106-4c33-9f88-7aa757587287", element="4356a278-4651-4d7f-9769-c6e97f1a5cdd")>
           

关于等待条件,其实还有很多,具体百度有。

前进和后退

Selenium中提供了back()方法后退,使用forward()方法前进。如下所示:

import  time
from  selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.baidu.com/')
browser.get('https://www.taobao.com/')
browser.get('https://www.python.org/')
browser.back()
time.sleep(1)
browser.forward()
browser.close()           

这里我们连续访问3个页面,然后调用back()方法回到第二个页面,接下来再调用forward()方法又可以前进到第三个页面。

Cookies

使用Selenium,还可以方便地对Cookies进行操作,例如获取、添加、删除Cookies等。示例如下:

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germey'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())           

首先我们访问了知乎,然后加载完成之后,浏览器实际上已经生成了 Cookies 了,我们调用 get_cookies() 方法就可以获取所有的 Cookies,然后我们添加一个 Cookie,传入一个字典,有 name、domain、value 等内容。接下来我们再次获取所有的 Cookies,可以发现结果就多了这一项 Cookie。最后我们调用 delete_all_cookies() 方法,删除所有的 Cookies,再重新获取,结果就为空了。

输出结果如下:

[{'name': 'tgw_l7_route', 'value': '200d77f3369d188920b797ddf09ec8d1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'q_c1', 'value': '0a04eaa0d8794c92950f448476254113|1532313479000|1532313479000', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_xsrf', 'value': '5c49b528447609a4fdb0a69e347d2fe1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'r_cap_id', 'value': '"ZmJlNzYxMDk0OGQ5NDY2ODgxOWQ5NGUzZWJlNGY2NDU=|1532313479|f2d05a38cae52a8a465c107214fd78355ac08326"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'cap_id', 'value': '"MTdmZDEyMGE1MDM5NDZhZmJjMWNiYjYyZDE2ZGUxNGM=|1532313479|b564e56d6e509522c20b4934bef527223d6d7a20"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_cap_id', 'value': '"YjA4YmEyYjg2NjY5NGQ1NDk2N2FhYjg5ZDY4MWU4OTQ=|1532313479|24b4d2925e30855d55ea74c047406d614a5be5e8"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'd_c0', 'value': '"AFAmMLhT8Q2PTjXr7t-dcrv3usuUs8WokLE=|1532313482"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_zap', 'value': '8399c18a-f9ea-4bc7-bc88-2e1da0aef956', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utma', 'value': '51854390.321058288.1532313485.1532313485.1532313485.1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmb', 'value': '51854390.0.10.1532313485', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmc', 'value': '51854390', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmz', 'value': '51854390.1532313485.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmv', 'value': '51854390.000--|3=entry_date=20180723=1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}]
[{'name': 'tgw_l7_route', 'value': '200d77f3369d188920b797ddf09ec8d1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'q_c1', 'value': '0a04eaa0d8794c92950f448476254113|1532313479000|1532313479000', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_xsrf', 'value': '5c49b528447609a4fdb0a69e347d2fe1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'r_cap_id', 'value': '"ZmJlNzYxMDk0OGQ5NDY2ODgxOWQ5NGUzZWJlNGY2NDU=|1532313479|f2d05a38cae52a8a465c107214fd78355ac08326"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'cap_id', 'value': '"MTdmZDEyMGE1MDM5NDZhZmJjMWNiYjYyZDE2ZGUxNGM=|1532313479|b564e56d6e509522c20b4934bef527223d6d7a20"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_cap_id', 'value': '"YjA4YmEyYjg2NjY5NGQ1NDk2N2FhYjg5ZDY4MWU4OTQ=|1532313479|24b4d2925e30855d55ea74c047406d614a5be5e8"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'd_c0', 'value': '"AFAmMLhT8Q2PTjXr7t-dcrv3usuUs8WokLE=|1532313482"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_zap', 'value': '8399c18a-f9ea-4bc7-bc88-2e1da0aef956', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utma', 'value': '51854390.321058288.1532313485.1532313485.1532313485.1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmb', 'value': '51854390.0.10.1532313485', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmc', 'value': '51854390', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmz', 'value': '51854390.1532313485.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmv', 'value': '51854390.000--|3=entry_date=20180723=1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'name', 'value': 'germey', 'path': '', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}]
[]
           

选项卡管理

import time
from  selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(1)
browser.switch_to_window(browser.window_handles[0])
browser.get('https://python.org')