前段时间看到了大大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
属性:获取节点idlocation
属性:获取位置tag_name
属性:获取tag_namesize
属性:获取大小
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 图片