Selenium的使用

直接使用模拟浏览器运行的方式来实现,这样我们就可以做到浏览器看到是什么样,抓取的源码就是什么样,也就是可见即可爬。这样我们就不用再去管网页内部的 JavaScript 用了什么算法渲染页面,不用管网页后台的 Ajax 接口到底有哪些参数,利用模拟浏览器的方式我们都可以直接获取 JavaScript 渲染的最终结果,只要能在浏览器中看到,我们都能抓取

1.环境配置

使用chrome浏览器,下载跟chrome浏览器版本相匹配的chromedriver.exe,放到当前使用的python环境的Scripts目录下

然后在命令行里执行chromedriver,出现如下画面则说明配置成功

2.声明浏览器对象

Selenium 支持非常多的浏览器,如 Chrome、Firefox、Edge 等,还有手机端的浏览器 Android、BlackBerry 等,另外无界面浏览器 PhantomJS 也同样支持。

使用其他的浏览器也需要做相应的环境配置才行。

可以用如下的方式初始化:

from selenium import webdriver

browser = webdriver.Chrome()

 # browser = webdriver.Firefox()
# browser = webdriver.Edge()
# browser = webdriver.PhantomJS()
# browser = webdriver.Safari()
3.访问页面

弹出Chrome 浏览器,自动访问了淘宝,然后控制台输出了淘宝页面的源代码,随后浏览器关闭

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source)
browser.close()

4.查找节点

Selenium 可以驱动浏览器完成各种操作,比如填充表单、模拟点击等等,但是前提是需要知道节点所在的位置

4.1单个节点

所有获取单个节点的方法:
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

通用find_element() 方法,它需要传入两个参数,一个是查找的方式 By,另一个就是值,实际上它就是 find_element_by_id() 这种方法的通用函数版本,比如 find_element_by_id(id) 就等价于 find_element(By.ID, id)

4.2多个节点

find_elements_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_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')

from selenium import webdriver

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

5.节点交互

比较常见的用法有:
输入文字用 send_keys() 方法,
清空文字用 clear() 方法,
按钮点击用 click() 方法

官方文档的交互动作介绍:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement

import time

from selenium import webdriver

browser = webdriver.Chrome()
try:
    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()
finally:
    browser.close()

6.动作链

还有另外一些操作它是没有特定的执行对象的,比如鼠标拖拽、键盘按键等操作。这些动作用另一种方式来执行,那就是动作链

更多的动作链操作可以参考官方文档的动作链介绍:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
try:
    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()
finally:
    browser.close()

7.执行JavaScript

对于某些操作,Selenium API 是没有提供的,如下拉进度条等,可以直接模拟运行 JavaScript,使用 execute_script() 方法即可实现

from selenium import webdriver

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

利用了 execute_script() 方法将进度条下拉到最底部,然后弹出 alert 提示框

8.获取节点信息

8.1获取属性

使用 get_attribute() 方法来获取节点的属性,那么这个的前提就是先选中这个节点

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
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('href'))

8.2获取文本值

每个 WebEelement 节点都有 text 属性,我们可以通过直接调用这个属性就可以得到节点内部的文本信息了,就相当于 BeautifulSoup 的 get_text() 方法、PyQuery 的 text() 方法

from selenium import webdriver

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

8.3.获取ID、位置、标签名、大小

from selenium import webdriver

browser = webdriver.Chrome()
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)

9.切换Frame

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

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

browser = webdriver.Chrome()
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)

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

所以说当页面中包含子 Frame 时,如果我们想获取子Frame 中的节点,需要先调用 switch_to.frame() 方法切换到对应的 Frame,然后再进行操作即可

10.延时等待

在 Selenium 中,get() 方法会在网页框架加载结束之后就结束执行,此时如果获取 page_source 可能并不是浏览器完全加载完成的页面,如果某些页面有额外的 Ajax 请求,我们在网页源代码中也不一定能成功获取到。所以这里我们需要延时等待一定时间确保节点已经加载出来。

在这里等待的方式有两种,一种隐式等待,一种显式等待

10.1 隐式等待

如果 Selenium 没有在DOM 中找到节点,将继续等待,超出设定时间后则抛出找不到节点的异常, 默认的时间是 0

用 implicitly_wait() 方法实现了隐式等待

from selenium import webdriver

browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)

10.2 显式等待

指定好要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,那就返回查找的节点,如果到了规定时间依然没有加载出该节点,则会抛出超时异常。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser, 0.01)
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_conditions,比如在这里我们传入了 presence_of_element_located 这个条件,就代表节点出现的意思,其参数是节点的定位元组,也就是 ID 为 q 的节点搜索框。所以这样可以做到的效果就是,在 10 秒内如果 ID 为 q 的节点即搜索框成功加载出来了,那就返回该节点,如果超过10 秒还没有加载出来,那就抛出异常。

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

所有的加载条件:

等待条件

含义

title_is

标题是某内容

title_contains

标题包含某内容

presence_of_element_located

节点加载出,传入定位元组,如(By.ID, 'p')

visibility_of_element_located

节点可见,传入定位元组

visibility_of

可见,传入节点对象

presence_of_all_elements_located

所有节点加载出

text_to_be_present_in_element

某个节点文本包含某文字

text_to_be_present_in_element_value

某个节点值包含某文字

frame_to_be_available_and_switch_to_it frame

加载并切换

invisibility_of_element_located

节点不可见

element_to_be_clickable

节点可点击

staleness_of

判断一个节点是否仍在DOM,可判断页面是否已经刷新

element_to_be_selected

节点可选择,传节点对象

element_located_to_be_selected

节点可选择,传入定位元组

element_selection_state_to_be

传入节点对象以及状态,相等返回True,否则返回False

element_located_selection_state_to_be

传入定位元组以及状态,相等返回True,否则返回False

alert_is_present

是否出现Alert

更多详细的等待条件的参数及用法介绍可以参考官方文档:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions

11.前进后退

在Selenium中使用 back() 方法可以后退,forward() 方法可以前进

import time

from selenium import webdriver

browser = webdriver.Chrome()
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()

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

12.cookies

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

from selenium import webdriver

browser = webdriver.Chrome()
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,再重新获取,结果就为空了。

13.选项卡管理

import time

from selenium import webdriver

browser = webdriver.Chrome()
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')

我们访问了百度,然后调用了 execute_script() 方法,传入 window.open() 的 JavaScript 语句新开启一个选项卡,然后接下来我们想切换到该选项卡,可以调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表,要想切换选项卡只需要调用 switch_to.window() 方法,传入选项卡的代号即可。在这里我们将第二个选项卡代号传入,即跳转到了第二个选项卡,然后接下来在第二个选项卡下打开一个新的页面,然后切换回第一个选项卡可以重新调用 switch_to.window() 方法,再执行其他操作即可

14.异常处理

在使用 Selenium 过程中,难免会遇到一些异常,例如超时、节点未找到等错误,一旦出现此类错误,程序便不会继续运行了,所以异常处理在程序中是十分重要的

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException

browser = webdriver.Chrome()
try:
    browser.get('https://www.baidu.com')
except TimeoutException:
    print('Time Out')
try:
    browser.find_element_by_id('hello')
except NoSuchElementException:
    print('No Element')
finally:
    browser.close()

更多的异常可以参考官方文档:http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions

posted @ 2019-01-30 16:49  哈喽哈喽111111  阅读(489)  评论(0编辑  收藏  举报