爬虫-Selenium


前段时间看到了大大wlop使用PS做画的视频,很令人惊叹,这些画在deviantart中可以免费获取2K高清,而deviantart的gallery中的图片每页显示数量有限,需要下拉(下拉动作时ajax获取下一页图片),ajax有一个参数iid无法对其解密。所以学习崔大 selenium 的教程准备来爬取美图。顺便宣传下wlop的鬼刀的宣传视频漫画《鬼刀》宣传视频


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

0. Overlook

官方手册: https://selenium-python.readthedocs.io/

import selenium
# dir(selenium)
[item for item in dir(selenium) if not item.startswith('__')]
['common', 'webdriver']

selenium 里有两个包: common, webdrive

dir(selenium.common)[:5] + ['...']  # common 下面只有一个包 exceptions, 用来存储异常的类
dir(selenium.common.exceptions)[:5] + ['...']  # 一些异常状态的类,可以用 `try`-`except` 来捕获错误信息
['ElementClickInterceptedException',
 'ElementNotInteractableException',
 'ElementNotSelectableException',
 'ElementNotVisibleException',
 'ErrorInResponseException',
 '...']
dir(selenium.webdriver)[:5] + ['...']
['ActionChains',
 'Android',
 'BlackBerry',
 'Chrome',
 'ChromeOptions',
 'DesiredCapabilities',
 'Edge',
 'Firefox',
 'FirefoxOptions',
 'FirefoxProfile',
 '...']

selenium.webdriver里面有各种浏览器对接的类:Android, BlackBerry, Chrome,以及对应浏览器的一些设置:ChromeOptions等

还有浏览器的辅助功能,常用的如下:

# dir(selenium.webdriver.common)
[item for item in dir(selenium.webdriver.common) if not item.startswith('__')]

# dir(selenium.webdriver.common.by)
# help(webdriver.common.by.By)  # 定位策略: ID,name,classname 等

# dir(selenium.webdriver.common.keys)
# help(selenium.webdriver.common.keys.Keys)  # 模拟按键
['action_chains',
 'actions',
 'alert',
 'by',
 'desired_capabilities',
 'html5',
 'keys',
 'proxy',
 'service',
 'touch_actions',
 'utils']
[item for item in dir(selenium.webdriver.support) if not item.startswith('__')]

# dir(selenium.webdriver.support.wait)
# help(selenium.webdriver.support.wait.WebDriverWait)  # 浏览器的触发时间,比如等待网页出现某个元素后再采取的动作
['select', 'ui', 'wait']

1. 使用示例

import time
from selenium import webdriver  # 驱动浏览器
from selenium.webdriver.common.by import By  # 定位元素的策略
from selenium.webdriver.common.keys import Keys  # 模拟按键
from selenium.webdriver.support import expected_conditions as EC  # 网页事件监控
from selenium.webdriver.support.wait import WebDriverWait


browser = webdriver.Chrome()
try:
    browser.get('http://www.baidu.com')
    input_entry = browser.find_element_by_id('kw')
    input_entry.send_keys('python')
    input_entry.send_keys(Keys.ENTER)
    wait = WebDriverWait(browser, 10)
    wait.until(EC.presence_of_element_located((By.ID, 'content_left')))
    # #content_left包含了所有搜索结果的条目
    print(browser.title)
    print(browser.current_url)
    print(browser.get_cookies()[:1])
    print(browser.page_source[:100])  # 网页源码
    time.sleep(3)
finally:
    browser.close()
python_百度搜索
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=python&rsv_pq=afa7486000050a2b&rsv_t=bd48aDp%2FU6bGzY8h%2FpUbblZQL4TyzDzpMOvoB70GiOLEY6Onc5GFbVJRTQg&rqlang=cn&rsv_enter=1&rsv_sug3=6&rsv_sug2=0&inputT=206&rsv_sug4=207
[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '1432_21079_27508'}]
<!DOCTYPE html><!--STATUS OK--><html xmlns="http://www.w3.org/1999/xhtml"><head><script charset="utf

2. 声明浏览器对象

from selenium import webdriver

# Chrome浏览器
browser = webdriver.Chrome()
browser.close()
# Edge浏览器
# browser_edge = webdriver.Edge()

如果正确配置过 selenium 驱动,声明对象后,会弹出对应浏览器。
如果不想其弹出来,可以配置 Headless 让其在使用过程中不打开浏览器。

from selenium import webdriver

# define headless
chrome_option = webdriver.ChromeOptions()
chrome_option.add_argument('--headless')

# add the option when creating driver
browser = webdriver.Chrome(chrome_options=chrome_option)
# browser.close()

3. 访问页面

get() 方法

browser.get('https://www.baidu.com')
print(browser.title)
print(browser.current_url)
print(browser.page_source[:100])
百度一下,你就知道
https://www.baidu.com/
<!DOCTYPE html><!--STATUS OK--><html xmlns="http://www.w3.org/1999/xhtml"><head>

    <meta http

4. 查找节点

在浏览器点击、输入之前需要定位到这些节点的位置。selenium 提供了很多定位策略,如ID、class_name、css选择器、xpath

4.1 单个节点

find_element_by...,只返回第一个满足的节点

input_class = browser.find_element_by_class_name('s_ipt')
input_name = browser.find_element_by_name('wd')
input_id = browser.find_element_by_id('kw')
input_css = browser.find_element_by_css_selector('#kw')
input_xpath = browser.find_element_by_xpath('//*[@id="kw"]')
print(input_class, input_name, input_id, input_css, input_xpath, sep='\n')
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>

selenium 提供了一个通用方法: find_element(),使用时传入查找方式By和值。例如使用ID来定位用通用方法写成如下:

from selenium.webdriver.common.by import By

input_1 = browser.find_element(By.ID, 'kw')
print(input_1)
<selenium.webdriver.remote.webelement.WebElement (session="7c77a97f6d8923f8abb56bbbfcf0c347", element="0.24960484176459885-1")>

4.2 多个节点

find_elements_by...,查找所有满足条件的节点,返回列表。
其实查找多个节点的方法就是单个节点方法中 element 加s。其通用方法也是这样。

5. 节点交互

常用动作:

  • 输入文字以及按键: send_keys()方法
  • 清空文字: clear()方法
  • 点击按钮: click()方法
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

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

# 百度搜索框输入python
input_entry = browser.find_element_by_id('kw')
input_entry.send_keys('python')
time.sleep(2)

# 清除文字,搜索selenium
input_entry.clear()
input_entry.send_keys('selenium')
time.sleep(2)

# 点击“百度一下按钮”
btn = browser.find_element_by_id('su').click()
time.sleep(2)

browser.close()

输入文字后提交数据有三个方法:

  • 输入框 send_keys()方法 发送回车
  • 点击按钮
  • 输入框的 submit()方法

以百度搜索为例:

import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

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

# 法一是 `send_keys` 发送回车
input_entry.send_keys('selenium entry.send_keys()')
input_entry.send_keys(Keys.ENTER)
time.sleep(2)

# 法二是定位到搜索按钮,执行 `click()` 方法
input_entry.clear()
input_entry.send_keys('selenium btn.click()')
btn = browser.find_element_by_id('su').click()
time.sleep(2)

# 法三,submit方法提交。需要输入框本身有submit方法
input_entry.clear()
input_entry.send_keys('selenium entry.submit()')
input_entry.submit()
time.sleep(2)

browser.close()

6. 动作链

鼠标拖拽。用来后期解决验证码自动识别问题等。

import time
from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
test_url = 'http://www.runoob.com/try/try.php?filename=jqueryui-example-droppable'
browser.get(test_url)

# runoob的这个拖拽示例的拖拽部件是在一个 iframe 里(网页中的网页),所以需要先跳转到此处
browser.switch_to.frame('iframeResult')
# 拖拽起点
source = browser.find_element_by_id('draggable')
# 拖拽终点
target = browser.find_element_by_id('droppable')
# 定义动作链
actions = ActionChains(browser)
# 定义动作与其起点与终点
actions.drag_and_drop(source, target)
# 执行动作
actions.perform()
time.sleep(3)

# 拖拽终点也可由 offset 来控制
actions.drag_and_drop_by_offset(source, 150, 50)
# 执行动作
actions.perform()

browser.close()

7. 执行 JavaScript

execute_script() 可以模拟运行js,来下拉页面等等

import time
from selenium import webdriver


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

input_entry = browser.find_element_by_id('kw')
input_entry.send_keys('test execute_script')
input_entry.submit()
time.sleep(2)

browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("To buttom")')

8. 获取节点属性

在 ##4.查找节点 中定位节点,返回的对象的是selenium.webdriver.remote.webelement.WebElement,可以使用 selenium 中的方法和属性来获取。

8.1 获取属性

get_attribute() 方法

from selenium import webdriver

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

nav_target = browser.find_element_by_class_name('mnav')
nav_target = browser.find_element_by_class_name('mnav')
print(nav_target.get_attribute('class'))
print(nav_target.get_attribute('name'))

8.2 获取文本值

text 属性

print(nav_target.text)
新闻

8.3 获取id、位置、标签名和大小

  • id属性:获取节点id
  • location属性:获取位置
  • tag_name属性:获取tag_name
  • size属性:获取大小
print(nav_target.id)
print(nav_target.location)
print(nav_target.tag_name)
print(nav_target.size)
0.5500508866283813-1
{'x': 347, 'y': 19}
a
{'height': 24, 'width': 26}

9. 切换 frame

网页里有种节点叫做 iframe,也就是 子frame,页面的子页面,它的结构与外部页面的结构一模一样。selenium打开页面时默认是在父级 frame 里操作,是不能跨frame来访问元素的,也就是不能直接访问 iframe 的元素。此时需要使用 switch_to.frame() 方法来切换到子frame,返回父frame使用 switch_to.parent_frame()

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


browser = webdriver.Chrome()
test_url = 'http://www.runoob.com/try/try.php?filename=jqueryui-example-droppable'
browser.get(test_url)


browser.switch_to.frame('iframeResult')
try:
    logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
    print('iframe has no logo element!\nSwitch to parent_frame.')
    browser.switch_to.parent_frame()
    logo = browser.find_element_by_class_name('logo')

print(logo.text)
browser.close()
iframe has no logo element!
Switch to parent_frame.
RUNOOB.COM

10. 延时等待

如果某些页面中有 Ajax 请求,对应的节点不一定已经被加载,所以需要等待节点加载完成再对其进行查找和操作

等待方式有两种:

  • 隐式等待:定时查找。
  • 显式等待:时间段内查找

10.1 隐式等待

如果没有找到节点,就等待一段时间再次查找。

from selenium import webdriver

browser = webdriver.Chrome()

browser.implicitly_wait(5)  # 隐式等待5秒
browser.get('http://www.zhihu.com/explore')
search_entry = browser.find_element_by_id('q')
print(search_entry)
<selenium.webdriver.remote.webelement.WebElement (session="43972286b45d3a0c393981af94a32ebd", element="0.6538876475888395-1")>

10.2 显式等待

指定一个最长等待时间,如果在这个时间内加载了出来,就返回查找的节点。
等待条件见官方文档:7.39. Expected conditions Support

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


chrome_option = webdriver.ChromeOptions()
chrome_option.add_argument('--headless')

browser = webdriver.Chrome(chrome_options=chrome_option)
browser.get('https://www.taobao.com')

wait = WebDriverWait(browser, 5)
input_entry = wait.until(EC.presence_of_element_located((By.ID, 'q')))
btn = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'tb-bg')))
print(input_entry, btn, sep='\n')
browser.close()
<selenium.webdriver.remote.webelement.WebElement (session="91da4b3d58f329b730691ac628203e79", element="0.3963876577824901-1")>
<selenium.webdriver.remote.webelement.WebElement (session="91da4b3d58f329b730691ac628203e79", element="0.3963876577824901-2")>

11. 前进和后退

模拟浏览器的前进后退
相关方法:
- back():回退
- forward():前进

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
time.sleep(1)
browser.get('https://www.zhihu.com')
time.sleep(2)
browser.back()
time.sleep(2)
browser.forward()
browser.close()

12. Cookies

常用的方法和属性:
- get_cookies():获取当前页面的 Cookies
- delete_all_cookies():删除所有 Cookies
- add_cookies():添加 Cookies

from selenium import webdriver

chrome_option = webdriver.ChromeOptions()
chrome_option.add_argument('--headless')
browser = webdriver.Chrome(chrome_options=chrome_option)
browser.get('https://www.baidu.com')

print(browser.get_cookies()[:2])
browser.delete_all_cookies()
browser.add_cookie({'name': '1107', 'value': 'gg'})
print(browser.get_cookies())

browser.close()
[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '1422_21080_27400_27409'}, {'domain': '.baidu.com', 'httpOnly': False, 'name': 'delPer', 'path': '/', 'secure': False, 'value': '0'}]
[{'domain': 'www.baidu.com', 'expiry': 2172311103, 'httpOnly': False, 'name': '1107', 'path': '/', 'secure': True, 'value': 'gg'}]

13. 选项卡管理

常用的方法和属性:
- execute_script('window.open()')方法:打开新选项卡
- window_handles属性:返回当前selenium打开的所有选项卡的句柄
- current_window_handle属性:返回当前选项看卡的句柄
- switch_to_window()方法:切换选项卡

注意:
- 创建新选项卡后需要切换到新选项卡才能操作,否则还是在原选择卡。
- 关闭时每个选项卡都需要关闭。

import time
from selenium import webdriver


browser = webdriver.Chrome()

browser.get('https://www.baidu.com')
print(browser.current_window_handle, browser.current_url, sep=';')
time.sleep(2)

browser.execute_script('window.open()')
print('创建但未切换:')
print(browser.current_window_handle, browser.current_url, sep=';')
time.sleep(2)

browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.zhihu.com')
print('创建后再切换:')
print(browser.current_window_handle, browser.current_url, sep=';')
time.sleep(2)

# 关闭所有选项卡
for handle in browser.window_handles:
    browser.switch_to_window(handle)
    browser.close()
CDwindow-EA4CCA1CB0C1A01A9425B40C071E835B;https://www.baidu.com/
创建但未切换:
CDwindow-EA4CCA1CB0C1A01A9425B40C071E835B;https://www.baidu.com/
创建后再切换:
CDwindow-5FDBCF0AF4A37B50090C38CA2D1BF3AE;https://www.zhihu.com/signup?next=%2F

14. 异常处理

try except捕获异常。可能的异常储存在 selenium.common.exceptions

本周计划使用 selenium 爬取 deviantart 图片

评论
还没有评论
    发表评论 说点什么