【Python爬虫学习笔记13】使用Selenium模拟浏览器行为

在上一篇笔记《Ajax数据爬取简介》中我们提到,在爬取动态渲染页面的数据时(通常为Ajax),我们可以使用AJAX URL分析法和Selenium模拟浏览器行为两种方法,其中前者已经分析一般思维已叙述,在本节中我们主要介绍如何使用Selenium模拟浏览器行为来获取数据。

一、准备工作

在正式介绍使用之前,我们需要先安装selenium库,安装库的过程和之前一样,我们可以直接使用命令’pip install selenium’。安装完成后我们还需要配置好用于驱动浏览器行为的驱动器driver,每一个浏览器对应的driver都是不同的,具体可以参看下表:

Browser Download URL
Chrome https://sites.google.com/a/chromium.org/chromedriver/downloads
Edge https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Firefox https://github.com/mozilla/geckodriver/releases
Safari https://webkit.org/blog/6900/webdriver-support-in-safari-10/

下载完对应版本的XXXdriver.exe文件后,将其放入我们python安装路径的scripts目录下即可,以供全局使用。例如,以Chrome为例,我的chromedriver.exe便可放在’C:\Users\UnikFox\AppData\Local\Programs\Python\Python36\Scripts’。

此外,这里要留意的是,每个浏览器版本的driver也是不同的,在下载页可以看到各driver所支持的浏览器版本。

二、基本使用

1.启动浏览器并访问页面

模拟浏览器的启动只需要实例化selenium的webdriver模块中相应的对象即可,然后通过这个对象的get(url)方法便可请求URL映射的页面,之后我们就可以通过访问这一个对象的属性来获取相应的页面数据信息。这里我们仍以Chrome为例,运行后我们便可发现其自动地打开了一个Chrome浏览器,并在顶部显示“Chrome正受到自动测试软件地字样”,说明我们使用selenium成功地打开了浏览器,同时我们还会发现也已经进入了我们所请求的页面。

## 模拟启动浏览器
# 导入webdriver模块
from selenium import webdriver

# 实例化webdriver对象
browser = webdriver.Chrome()

# 请求页面
browser.get('https://www.baidu.com')

# 获取网页源代码
html_text = browser.page_source

【Python爬虫学习笔记13】使用Selenium模拟浏览器行为

2.关闭页面

在我们模拟完成后,可以使用browser对象的close()和quit()方法关闭页面,其主要区别是前者为关闭当前页面,而后者为退出整个浏览器。

# 关闭当前页面
browser.close()

# 退出整个浏览器
browser.quit()

3.解析元素

在我们获取到页面的HTML源文本后,如要对其进行解析,实际上我们可以使用之前介绍的lxml和beautifulsoup两种解析方法,不过这里我们主要来说明一下使用selenium的解析策略。

在selenium中,我们解析元素都是通过browser来实现的,解析的方法可以是通过元素id、元素名name、类名class_name、CSS选择器或者xpath语法,在实际中具体使用时我们只需要调用browser的相关方法即可。

例如,如果我们要解析百度中的输入框input元素,则可以使用如下的解析方法:

HTML源文本:

<span class="bg s_ipt_wr quickdelete-wrap">

    <span class="soutu-btn"></span>

    <input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">

    <a href="javascript:;" id="quickdelete" title="清空" class="quickdelete" style="top: 0px; right: 0px; display: none;"></a>

</span>

## 解析元素
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')

# 通过id解析
inputTag = browser.find_element_by_id('kw')

# 通过name解析
inputTag = browser.find_element_by_name('wd')

# 通过class_name解析
inputTag = browser.find_element_by_class_name('s_ipt')

# 通过CSS选择器解析
inputTag = browser.find_element_by_css_selector('.quickdelete-wrap > input')

# 通过xpath解析
inputTag = browser.find_element_by_xpath('//input[@id="kw"]')

print(type(inputTag))
# Output: <class 'selenium.webdriver.remote.webelement.WebElement'>

print(inputTag)
# Output: <selenium.webdriver.remote.webelement.WebElement (session="446090034e5c7eb8f63ae5975428cd5d", element="0.07898054563855905-1")>

其实,在实际爬虫中我们更多的情况是需要解析多个元素,此时我们便可以通过’browser.find_elements_by_XXX()’来解析,和单个元素解析不同,其返回的是一个WebElement元素列表,即find_element是获取第一个满足条件的元素,而find_elements则是获取所有满足条件的元素。

此外,通过上述这种解析可能不够灵活,因为它都将解析方法都固定了,实际上selenium还为我们提供了一种可灵活选择解析方法的解析函数,这样上述示例便可等价为:

## 使用By来解析元素
from selenium import webdriver
# 导入By对象
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')

# 通过id解析
inputTag = browser.find_element(By.ID,'su')

# 通过name解析
inputTag = browser.find_element(By.NAME,'wd')

# 通过class_name解析
inputTag = browser.find_element(By.CLASS_NAME,'s_ipt')

# 通过CSS选择器解析
inputTag = browser.find_element(By.CSS_SELECTOR,'.quickdelete-wrap > input')

# 通过xpath解析
inputTag = browser.find_element(By.XPATH,'//input[@id="kw"]')

4.提取元素信息

在我们获取到元素后,就要对元素的内容进行提取,提取对象包括元素的文本信息和元素的属性信息,分别对应着text属性和get_attribute(),使用示例如下:

HTML源文本:

<p id="cp">©2018&nbsp;Baidu&nbsp;使用百度前必读&nbsp;意见反馈&nbsp;京ICP证030173号&nbsp;&nbsp;京公网安备11000002000001号&nbsp;</p>

 

copyright = browser.find_element_by_id('cp')
print(copyright.text)       #©2018 Baidu 使用百度前必读 意见反馈 京ICP证030173号  京公网安备11000002000001号
print(inputTag.get_attribute('id'))       #cp

5.操作元素

元素的操作主要是点击和输入,在selenium中实现方式也很简便,只需要对相应待操作元素使用click()和send_keys()方法即可,使用示例如下:

#使用send_keys和click方法模拟输入和点击

inputTag = browser.find_element_by_id('kw')
inputTag.send_keys('python')

inputTag.click()

此外,在输入中若要清除已输入的内容,则可以使用clear()方法。对于任何与点击操作有关的行为都可以用这种方法实现,比如说单选和复选,前提只需要获取相应的元素就行了。

除了表单和单复选外,处理较多的还有下拉选择,因下拉点击后还需要选中元素,我们不能直接进行操作。不过我们可以借助selenium的一个类selenium.webdriver.support.ui.Select来实现,先将下拉选择元素作为参数传入这个类中实例化对象,然后我们就可以使用这个对象进行选择了。使用示例如下:

## 模拟下拉选择操作
from selenium import webdriver
# 导入selenium提供的下拉选择类
from selenium.webdriver.support.ui import Select

# <此处省略部分代码>

# 选中下拉标签并实例化Select对象
selectTag = Select(driver.find_element_by_name("FoodMenu"))

# 三种选择方式
# 根据序列索引选择(从0开始)
selectTag.select_by_index(1)
# 根据属性值value选择
selectTag.select_by_value("Fish")
# 根据可视文本选择
selectTag.select_by_visible_text("")

# 取消选中所有选项
selectTag.deselect_all()

三、其他使用

1.行为链

首先,什么是行为链呢?在上述的示例中,我们都是通过对单个元素执行单步操作完成的,其实还存有另一些操作,它们没有特定的执行对象,比如鼠标拖拽、键盘按键等,这些动作是用另一种方式来执行的,常分为多步,这就是行为链,又称为动作链。

由于行为链在爬虫中运用得不多,在此以将鼠标移动到某个元素上并执行点击事件为示例简单介绍一下:

## 使用行为链示例
from selenium import webdriver
# 导入行为链模块
from selenium.webdriver import ActionChains

# <此处省略部分代码>

#获取操作对象元素
inputTag = browser.find_element_by_id('kw')
submitTag = browser.find_element_by_id('su')

# 实例化行为链,传入参数webdriver对象
actions = ActionChains(browser)

# 有序化定义行为链对象得执行步骤
actions.move_to_element(inputTag)
actions.send_keys_to_element(inputTag,'python')
actions.move_to_element(submitTag)
actions.click(submitTag)

# 执行行为链
actions.perform()

此外,还有如下得常见鼠标操作得方法:

点击但不松开鼠标:click_and_hold(element)
右键点击:context_click(element)
双击:double_click(element)

2.Cookie操作

使用Selenium我们可以很方便地对Cookie进行获取、添加和删除操作,使用示例如下:

## Cookie操作
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')

# 获取所有Cookie
print(browser.get_cookies())
#[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '26523_1465_21094_27244_20930'}, ...,{'domain': '.baidu.com', 'expiry': 3686218697.591083, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1538735048'}]

# 获取指定名的Cookie
print(browser.get_cookie('PSTM'))
#{'domain': '.baidu.com', 'expiry': 3686218697.591083, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1538735048'}

# 添加Cookie
browser.add_cookie({'name':'name','domain':'www.baidu.com','value':'UnikFox'})
print(browser.get_cookies())
#[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '26523_1465_21094_27244_20930'}...{'domain': 'www.baidu.com', 'expiry': 2169455052, 'httpOnly': False, 'name': 'name', 'path': '/', 'secure': True, 'value': 'UnikFox'}]

# 删除指定名的Cookie
browser.delete_cookie('name')
print(browser.get_cookies())
#[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '26523_1465_21094_27244_20930'}, ...,{'domain': '.baidu.com', 'expiry': 3686218697.591083, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1538735048'}]

# 删除所有Cookie
browser.delete_all_cookies()
print(browser.get_cookies())
#[]

3.页面等待

由于很多时候我们无法预知页面元素需要花多长的时间进行加载,这时候我们就需要使用等待技术等待页面加载完成在进行解析。常用的等待方式分为显式等待和隐式等待。其中,显式等待即静态等待,无论是否加载完成都会等待,类似于time.sleep();而隐式等待则是条件等待,通常会设置一个条件,在等待的期间当条件满足时就会结束等待,若等待时间到了条件仍未满足就会抛出TimeoutException错误。

使用示例如下:

## 页面等待
from selenium import webdriver

# 导入等待类
from selenium.webdriver.support.ui import WebDriverWait
# 导入判断条件
from selenium.webdriver.support import expected_conditions as EC
# 导入检索条件
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.douban.com')

# 隐式等待
browser.implicitly_wait(10)
browser.find_element_by_id('123456')

# 显式等待加载
element= WebDriverWait(browser,10).until(
    # 调用判断条件检查元素是否已加载显示
    EC.presence_of_element_located((By.ID,'123456'))    #传入检索元组(方法,值)
)
print(element)

browser.quit()

一些其他的等待条件:

presence_of_element_located():某个元素已加载完毕
presence_of_all_emement_located():网页中所有满足条件的元素都已加载完毕
element_to_be_cliable():某个元素已可以点击

4.打开新标签页面与切换页面

如果有时候一个浏览器需要打开不同的标签页面(这里不是新打开一个浏览器),我们可以借助browser的execute_script("window.open(url)")方法来实现,不过要注意的是,虽然打开了一个新的标签页面,但并没有切换过去,browser访问的还是原来的页面,我们可以通过browser.current_url来查看当前页面URL。

对于拥有多个子标签的页面,如果我们想要切换不同的页面,可以借助selenium提供的switch_to_window()切换方法,该方法接收待切换页面的对象,具体可通过browser.window_handles列表索引得到,而这给列表里的元素是按照browser访问页面的先后顺序自动定的。

使用示例如下:

## 打开新标签页面和切换页面
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')

#打开一个新的标签页面
browser.execute_script("window.open('https://www.douban.com')")

# 查看当前访问得页面
print(browser.current_url)    # https://www.baidu.com/

# 查看window_handles记录列表
print(browser.window_handles) # ['CDwindow-9F7F732D4DCD86446392EADF4D719F07', 'CDwindow-A61A3EE061BC15495FF17AB629309A0B']

#切换页面
browser.switch_to.window(browser.window_handles[1])
print(browser.current_url)    # https://www.douban.com/

5.使用代理

在Selenium中我们也可以进行代理设置,不同的浏览器设置代理得方式不同,这里我们以Chrome为例介绍设置代理得方法。

在Chrome中设置代理,首先需要获取设置对象webdriver.ChromeOptions(),再使用该对象得add_argument()方法对其添加代理参数,最后再实例化浏览器驱动对象webdrive.Chrome(),同时传入参数设置对象即可。

使用示例如下:

from selenium import webdriver

# 不使用代理
browser = webdriver.Chrome()
browser.get('http://httpbin.org/ip')
"""
{
  "origin": "36.157.132.78"
}
"""

# 使用代理
# 获取设置对象
options = webdriver.ChromeOptions()
# 新增设置参数
options.add_argument("--proxy-server=http://134.175.68.57:80")
# 实例化浏览器并传入设置选择参数
browser = webdriver.Chrome(chrome_options=options)
browser.get('http://httpbin.org/ip')
"""
{
  "origin": "134.175.68.57"
}
"""