selenium用法详解【从入门到实战】【Python爬虫】【4万字】

2022年05月11日 阅读数:5
这篇文章主要向大家介绍selenium用法详解【从入门到实战】【Python爬虫】【4万字】,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

👉跳转文末👈 获取实战源码与做者联系方式,共同窗习进步javascript

在这里插入图片描述



简介

Selenium 是最普遍使用的开源 Web UI(用户界面)自动化测试套件之一。Selenium 支持的语言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驱动程序最受 Python 和 C#欢迎。 Selenium 测试脚本可使用任何支持的编程语言进行编码,而且能够直接在大多数现代 Web 浏览器中运行。在爬虫领域 selenium 一样是一把利器,可以解决大部分的网页的反爬问题。下面就进入正式的 study 阶段。css



selenium安装

打开 cmd,输入下面命令进行安装。html

pip install -i https://pypi.douban.com/simple selenium

执行后,使用 pip show selenium 查看是否安装成功。java



安装浏览器驱动

针对不一样的浏览器,须要安装不一样的驱动。下面列举了常见的浏览器与对应的驱动程序下载连接,部分网址须要 “科学上网” 才能打开哦(dddd)。node

  • Firefox 浏览器驱动:Firefox
  • Chrome 浏览器驱动:Chrome
  • IE 浏览器驱动:IE
  • Edge 浏览器驱动:Edge
  • PhantomJS 浏览器驱动:PhantomJS
  • Opera 浏览器驱动:Opera

这里以安装 Chrome 驱动做为演示。但 Chrome 在用 selenium 进行自动化测试时仍是有部分 bug ,常规使用没什么问题,但若是出现一些不多见的报错,可使用 Firefox 进行尝试,毕竟是 selenium 官方推荐使用的。python

肯定浏览器版本

在新标签页输入 chrome://settings/ 进入设置界面,而后选择 【关于 Chromegit

查看本身的版本信息。这里个人版本是94,这样在下载对应版本的 Chrome 驱动便可。
在这里插入图片描述github

下载驱动

打开 Chrome驱动 。单击对应的版本。
在这里插入图片描述
根据本身的操做系统,选择下载。web

在这里插入图片描述
下载完成后,压缩包内只有一个 exe 文件。
在这里插入图片描述
chromedriver.exe 保存到任意位置,并把当前路径保存到环境变量中(个人电脑>>右键属性>>高级系统设置>>高级>>环境变量>>系统变量>>Path),添加的时候要注意不要把 path 变量给覆盖了,若是覆盖了千万别关机,而后百度。添加成功后使用下面代码进行测试。面试

from selenium import webdriver

# Chrome浏览器
driver = webdriver.Chrome()


定位页面元素

打开指定页面

使用 selenium 定位页面元素的前提是你已经了解基本的页面布局及各类标签含义,固然若是以前没有接触过,如今我也能够带你简单的了解一下。
以咱们熟知的 CSDN 为例,咱们进入首页,按 【F12】 进入开发者工具。红框中显示的就是页面的代码,咱们要作的就是从代码中定位获取咱们须要的元素。
在这里插入图片描述
想要定位并获取页面中的信息,首先要使用 webdriver 打开指定页面,再去定位。

from selenium import webdriver

# Chrome浏览器
driver = webdriver.Chrome()
driver.get('https://www.csdn.net/')

执行上面语句后会发现,浏览器打开 CSDN 主页后会立刻关闭,想要防止浏览器自动关闭,能够添加下面代码。

# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)

# 将option做为参数添加到Chrome中
driver = webdriver.Chrome(chrome_options=option)

这样将上面的代码组合再打开浏览器就不会自动关闭了。

from selenium import webdriver

# 不自动关闭浏览器
option = webdriver.ChromeOptions()
option.add_experimental_option("detach", True)

# 注意此处添加了chrome_options参数
driver = webdriver.Chrome(chrome_options=option)
driver.get('https://www.csdn.net/')

下面咱们再来看看几种常见的页面元素定位方式。

id 定位

标签的 id 具备惟一性,就像人的身份证,假设有个 input 标签以下。

<input id="toolbar-search-input" autocomplete="off" type="text" value="" placeholder="C++难在哪里?">

咱们能够经过 id 定位到它,因为 id 的惟一性,咱们能够不用管其余的标签的内容。

driver.find_element_by_id("toolbar-search-input")

name 定位

name 指定标签的名称,在页面中能够不惟一。假设有个 meta 标签以下

<meta name="keywords" content="CSDN博客,CSDN学院,CSDN论坛,CSDN直播">

咱们可使用 find_element_by_name 定位到 meta 标签。

driver.find_element_by_name("keywords")

class 定位

class 指定标签的类名,在页面中能够不惟一。假设有个 div 标签以下

<div class="toolbar-search-container">

咱们可使用 find_element_by_class_name 定位到 div 标签。

driver.find_element_by_class_name("toolbar-search-container")

tag 定位

每一个 tag 每每用来定义一类功能,因此经过 tag 来识别某个元素的成功率很低,每一个页面通常都用不少相同的 tag ,好比:\<div\>\<input\> 等。这里仍是用上面的 div 做为例子。

<div class="toolbar-search-container">

咱们可使用 find_element_by_class_name 定位到 div 标签。

driver.find_element_by_tag_name("div")

xpath 定位

xpath 是一种在 XML 文档中定位元素的语言,它拥有多种定位方式,下面经过实例咱们看一下它的几种使用方式。

<html>
  <head>...<head/>
  <body>
    <div id="csdn-toolbar">
      <div class="toolbar-inside">
        <div class="toolbar-container">
          <div class="toolbar-container-left">...</div>
          <div class="toolbar-container-middle">
            <div class="toolbar-search onlySearch">
			<div class="toolbar-search-container">
				<input id="toolbar-search-input" autocomplete="off" type="text" value="" placeholder="C++难在哪里?">

根据上面的标签须要定位 最后一行 input 标签,如下列出了四种方式,xpath 定位的方式多样并不惟一,使用时根据状况进行解析便可。

# 绝对路径(层级关系)定位
driver.find_element_by_xpath(
	"/html/body/div/div/div/div[2]/div/div/input[1]")
# 利用元素属性定位
driver.find_element_by_xpath(
	"//*[@id='toolbar-search-input']"))
# 层级+元素属性定位
driver.find_element_by_xpath(
	"//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]")
# 逻辑运算符定位
driver.find_element_by_xpath(
	"//*[@id='toolbar-search-input' and @autocomplete='off']")

css 定位

CSS 使用选择器来为页面元素绑定属性,它能够较为灵活的选择控件的任意属性,通常定位速度比 xpath 要快,但使用起来略有难度。
CSS 选择器常见语法:

方法 例子 描述
.class .toolbar-search-container 选择 class = 'toolbar-search-container' 的全部元素
#id #toolbar-search-input 选择 id = 'toolbar-search-input' 的元素
* * 选择全部元素
element input 选择全部 <input\> 元素
element>element div>input 选择父元素为 <div\> 的全部 <input\> 元素
element+element div+input 选择同一级中在 <div\> 以后的全部 <input\> 元素
[attribute=value] type='text' 选择 type = 'text' 的全部元素

举个简单的例子,一样定位上面实例中的 input 标签。

driver.find_element_by_css_selector('#toolbar-search-input')
driver.find_element_by_css_selector('html>body>div>div>div>div>div>div>input')

link 定位

link 专门用来定位文本连接,假如要定位下面这一标签。

<div class="practice-box" data-v-04f46969="">加入!每日一练</div>

咱们使用 find_element_by_link_text 并指明标签内所有文本便可定位。

driver.find_element_by_link_text("加入!每日一练")

partial_link 定位

partial_link 翻译过来就是“部分连接”,对于有些文本很长,这时候就能够只指定部分文本便可定位,一样使用刚才的例子。

<div class="practice-box" data-v-04f46969="">加入!每日一练</div>

咱们使用 find_element_by_partial_link_text 并指明标签内部分文本进行定位。

driver.find_element_by_partial_link_text("加入")


浏览器控制

修改浏览器窗口大小

webdriver 提供 set_window_size() 方法来修改浏览器窗口的大小。

from selenium import webdriver

# Chrome浏览器
driver = webdriver.Chrome()
driver.get('https://www.csdn.net/')
# 设置浏览器浏览器的宽高为:600x800
driver.set_window_size(600, 800)

也可使用 maximize_window() 方法能够实现浏览器全屏显示。

from selenium import webdriver

# Chrome浏览器
driver = webdriver.Chrome()
driver.get('https://www.csdn.net/')
# 设置浏览器浏览器的宽高为:600x800
driver.maximize_window()

浏览器前进&后退

webdriver 提供 backforward 方法来实现页面的后退与前进。下面咱们 ①进入CSDN首页,②打开CSDN我的主页,③back 返回到CSDN首页,④ forward 前进到我的主页。

from selenium import webdriver
from time import sleep

driver = webdriver.Chrome()

# 访问CSDN首页
driver.get('https://www.csdn.net/')
sleep(2)
#访问CSDN我的主页
driver.get('https://blog.csdn.net/qq_43965708')
sleep(2)
#返回(后退)到CSDN首页
driver.back()
sleep(2)
#前进到我的主页
driver.forward()

请添加图片描述

细心的读者会发现第二次 get() 打开新页面时,会在原来的页面打开,而不是在新标签中打开。若是想的话也能够在新的标签页中打开新的连接,但须要更改一下代码,执行 js 语句来打开新的标签。

# 在原页面打开
driver.get('https://blog.csdn.net/qq_43965708')

# 新标签中打开
js = "window.open('https://blog.csdn.net/qq_43965708')"
driver.execute_script(js)

浏览器刷新

在一些特殊状况下咱们可能须要刷新页面来获取最新的页面数据,这时咱们可使用 refresh() 来刷新当前页面。

# 刷新页面
driver.refresh()

浏览器窗口切换

在不少时候咱们都须要用到窗口切换,好比:当咱们点击注册按钮时,它通常会打开一个新的标签页,但实际上代码并无切换到最新页面中,这时你若是要定位注册页面的标签就会发现定位不到,这时就须要将实际窗口切换到最新打开的那个窗口。咱们先获取当前各个窗口的句柄,这些信息的保存顺序是按照时间来的,最新打开的窗口放在数组的末尾,这时咱们就能够定位到最新打开的那个窗口了。

# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
driver.switch_to.window(windows[-1])

常见操做

webdriver中的常见操做有:

方法 描述
send_keys() 模拟输入指定内容
clear() 清除文本内容
is_displayed() 判断该元素是否可见
get_attribute() 获取标签属性值
size 返回元素的尺寸
text 返回元素文本

接下来仍是用 CSDN 首页为例,须要用到的就是搜素框和搜索按钮。经过下面的例子就能够气息的了解各个操做的用法了。
在这里插入图片描述

from selenium import webdriver

driver = webdriver.Chrome()

driver.get('https://www.csdn.net/')
sleep(2)
# 定位搜索输入框
text_label = driver.find_element_by_xpath('//*[@id="toolbar-search-input"]')

# 在搜索框中输入 Dream丶Killer
text_label.send_keys('Dream丶Killer')

sleep(2)
# 清除搜索框中的内容
text_label.clear()

# 输出搜索框元素是否可见
print(text_label.is_displayed())
# 输出placeholder的值
print(text_label.get_attribute('placeholder'))

# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 输出按钮的大小
print(button.size)
# 输出按钮上的文本
print(button.text)

'''输出内容
True
python面试100问
{'height': 32, 'width': 28}
搜索
'''

请添加图片描述



鼠标控制

在webdriver 中,鼠标操做都封装在ActionChains类中,常见方法以下:

方法 描述
click() 单击左键
context_click() 单击右键
double_click() 双击
drag_and_drop() 拖动
move_to_element() 鼠标悬停
perform() 执行全部ActionChains中存储的动做

单击左键

模拟完成单击鼠标左键的操做,通常点击进入子页面等会用到,左键不须要用到 ActionChains

# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 执行单击操做
button.click()

单击右键

鼠标右击的操做与左击有很大不一样,须要使用 ActionChains

from selenium.webdriver.common.action_chains import ActionChains

# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 右键搜索按钮
ActionChains(driver).context_click(button).perform()

双击

模拟鼠标双击操做。

# 定位搜索按钮
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span')
# 执行双击动做
ActionChains(driver).double_click(button).perform()

拖动

模拟鼠标拖动操做,该操做有两个必要参数,

  • source:鼠标拖动的元素
  • target:鼠标拖至并释放的目标元素
# 定位要拖动的元素
source = driver.find_element_by_xpath('xxx')
# 定位目标元素
target = driver.find_element_by_xpath('xxx')
# 执行拖动动做
ActionChains(driver).drag_and_drop(source, target).perform()

鼠标悬停

模拟悬停的做用通常是为了显示隐藏的下拉框,好比 CSDN 主页的收藏栏,咱们看一下效果。
在这里插入图片描述

# 定位收藏栏
collect  = driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/div[3]/div/div[3]/a')

# 悬停至收藏标签处
ActionChains(driver).move_to_element(collect).perform()


键盘控制

webdriverKeys 类几乎提供了键盘上的全部按键方法,咱们可使用 send_keys + Keys 实现输出键盘上的组合按键如 “Ctrl + C”、“Ctrl + V” 等。

from selenium.webdriver.common.keys import Keys

# 定位输入框并输入文本
driver.find_element_by_id('xxx').send_keys('Dream丶killer')

# 模拟回车键进行跳转(输入内容后) 
driver.find_element_by_id('xxx').send_keys(Keys.ENTER)

# 使用 Backspace 来删除一个字符
driver.find_element_by_id('xxx').send_keys(Keys.BACK_SPACE)

# Ctrl + A 全选输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'a')

# Ctrl + C 复制输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'c')

# Ctrl + V 粘贴输入框中内容
driver.find_element_by_id('xxx').send_keys(Keys.CONTROL, 'v')

其余常见键盘操做:

操做 描述
Keys.F1 F1键
Keys.SPACE 空格
Keys.TAB Tab键
Keys.ESCAPE ESC键
Keys.ALT Alt键
Keys.SHIFT Shift键
Keys.ARROW_DOWN 向下箭头
Keys.ARROW_LEFT 向左箭头
Keys.ARROW_RIGHT 向右箭头
Keys.ARROW_UP 向上箭头


设置元素等待

不少页面都使用 ajax 技术,页面的元素不是同时被加载出来的,为了防止定位这些尚在加载的元素报错,能够设置元素等来增长脚本的稳定性。webdriver 中的等待分为 显式等待 和 隐式等待。

显式等待

显式等待:设置一个超时时间,每一个一段时间就去检测一次该元素是否存在,若是存在则执行后续内容,若是超过最大时间(超时时间)则抛出超时异常(TimeoutException)。显示等待须要使用 WebDriverWait,同时配合 untilnot until 。下面详细讲解一下。

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)

  • driver:浏览器驱动
  • timeout:超时时间,单位秒
  • poll_frequency:每次检测的间隔时间,默认为0.5秒
  • ignored_exceptions:指定忽略的异常,若是在调用 untiluntil_not 的过程当中抛出指定忽略的异常,则不中断代码,默认忽略的只有 NoSuchElementException

until(method, message=’ ‘)
until_not(method, message=’ ')

  • method:指定预期条件的判断方法,在等待期间,每隔一段时间调用该方法,判断元素是否存在,直到元素出现。until_not 正好相反,当元素消失或指定条件不成立,则继续执行后续代码
  • message: 若是超时,抛出 TimeoutException ,并显示 message 中的内容

method 中的预期条件判断方法是由 expected_conditions 提供,下面列举经常使用方法。

先定义一个定位器

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

driver = webdriver.Chrome()
locator = (By.ID, 'kw')
element = driver.find_element_by_id('kw')
方法 描述
title_is(‘百度一下’) 判断当前页面的 title 是否等于预期
title_contains(‘百度’) 判断当前页面的 title 是否包含预期字符串
presence_of_element_located(locator) 判断元素是否被加到了 dom 树里,并不表明该元素必定可见
visibility_of_element_located(locator) 判断元素是否可见,可见表明元素非隐藏,而且元素的宽和高都不等于0
visibility_of(element) 跟上一个方法做用相同,但传入参数为 element
text_to_be_present_in_element(locator , ‘百度’) 判断元素中的 text 是否包含了预期的字符串
text_to_be_present_in_element_value(locator , ‘某值’) 判断元素中的 value 属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it(locator) 判断该 frame 是否能够 switch 进去,True 则 switch 进去,反之 False
invisibility_of_element_located(locator) 判断元素中是否不存在于 dom 树或不可见
element_to_be_clickable(locator) 判断元素中是否可见而且是可点击的
staleness_of(element) 等待元素从 dom 树中移除
element_to_be_selected(element) 判断元素是否被选中,通常用在下拉列表
element_selection_state_to_be(element, True) 判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False
element_located_selection_state_to_be(locator, True) 跟上一个方法做用相同,但传入参数为 locator
alert_is_present() 判断页面上是否存在 alert

下面写一个简单的例子,这里定位一个页面不存在的元素,抛出的异常信息正是咱们指定的内容。

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

driver = webdriver.Chrome()
element = WebDriverWait(driver, 5, 0.5).until(
            EC.presence_of_element_located((By.ID, 'kw')),
                                           message='超时啦!')

在这里插入图片描述

隐式等待

隐式等待也是指定一个超时时间,若是超出这个时间指定元素尚未被加载出来,就会抛出 NoSuchElementException 异常。
除了抛出的异常不一样外,还有一点,隐式等待是全局性的,即运行过程当中,若是元素能够定位到,它不会影响代码运行,但若是定位不到,则它会以轮询的方式不断地访问元素直到元素被找到,若超过指定时间,则抛出异常

使用 implicitly_wait() 来实现隐式等待,使用难度相对于显式等待要简单不少。
示例:打开我的主页,设置一个隐式等待时间 5s,经过 id 定位一个不存在的元素,最后打印 抛出的异常 与 运行时间。

from selenium import webdriver
from time import time

driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/qq_43965708')

start = time()
driver.implicitly_wait(5)
try:
    driver.find_element_by_id('kw')
except Exception as e:
    print(e)
    print(f'耗时:{time()-start}')

在这里插入图片描述
代码运行到 driver.find_element_by_id('kw') 这句以后触发隐式等待,在轮询检查 5s 后仍然没有定位到元素,抛出异常。

强制等待

使用 time.sleep() 强制等待,设置固定的休眠时间,对于代码的运行效率会有影响。以上面的例子做为参照,将 隐式等待 改成 强制等待。

from selenium import webdriver
from time import time, sleep

driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/qq_43965708')

start = time()
sleep(5)
try:
    driver.find_element_by_id('kw')
except Exception as e:
    print(e)
    print(f'耗时:{time()-start}')

在这里插入图片描述
值得一提的是,对于定位不到元素的时候,从耗时方面隐式等待和强制等待没什么区别。但若是元素通过 2s 后被加载出来,这时隐式等待就会继续执行下面的代码,但 sleep还要继续等待 3s。



定位一组元素

上篇讲述了定位一个元素的 8 种方法,定位一组元素使用的方法只须要将 element 改成 elements 便可,它的使用场景通常是为了批量操做元素。

  • find_elements_by_id()
  • find_elements_by_name()
  • find_elements_by_class_name()
  • find_elements_by_tag_name()
  • find_elements_by_xpath()
  • find_elements_by_css_selector()
  • find_elements_by_link_text()
  • find_elements_by_partial_link_text()

这里以 CSDN 首页的一个 博客专家栏 为例。
在这里插入图片描述
下面使用 find_elements_by_xpath 来定位三位专家的名称。
在这里插入图片描述
这是专家名称部分的页面代码,不知各位有没有想到如何经过 xpath 定位这一组专家的名称呢?

from selenium import webdriver

# 设置无头浏览器
option = webdriver.ChromeOptions()
option.add_argument('--headless')

driver = webdriver.Chrome(options=option)
driver.get('https://blog.csdn.net/')

p_list = driver.find_elements_by_xpath("//p[@class='name']")
name = [p.text for p in p_list]
name



切换操做

窗口切换

selenium 操做页面的时候,可能会由于点击某个连接而跳转到一个新的页面(打开了一个新标签页),这时候 selenium 实际仍是处于上一个页面的,须要咱们进行切换才可以定位最新页面上的元素。

窗口切换须要使用 switch_to.windows() 方法。

首先咱们先看看下面的代码。

代码流程:先进入 【CSDN首页】,保存当前页面的句柄,而后再点击左侧 【CSDN官方博客】跳转进入新的标签页,再次保存页面的句柄,咱们验证一下 selenium 会不会自动定位到新打开的窗口。

from selenium import webdriver

handles = []
driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/')
# 设置隐式等待
driver.implicitly_wait(3)
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)
# 点击 python,进入分类页面
driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click()
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)

print(handles)
# 获取当前全部窗口的句柄
print(driver.window_handles)

在这里插入图片描述
能够看到第一个列表 handle 是相同的,说明 selenium 实际操做的仍是 CSDN首页 ,并未切换到新页面。
下面使用 switch_to.windows() 进行切换。

from selenium import webdriver

handles = []
driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/')
# 设置隐式等待
driver.implicitly_wait(3)
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)
# 点击 python,进入分类页面
driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click()
# 切换窗口
driver.switch_to.window(driver.window_handles[-1])
# 获取当前窗口的句柄
handles.append(driver.current_window_handle)

print(handles)
print(driver.window_handles)

在这里插入图片描述
上面代码在点击跳转后,使用 switch_to 切换窗口,window_handles 返回的 handle 列表是按照页面出现时间进行排序的,最新打开的页面确定是最后一个,这样用 driver.window_handles[-1] + switch_to 便可跳转到最新打开的页面了。

那若是打开的窗口有多个,如何跳转到以前打开的窗口,若是确实有这个需求,那么打开窗口是就须要记录每个窗口的 key(别名) 与 value(handle),保存到字典中,后续根据 key 来取 handle

表单切换

不少页面也会用带 frame/iframe 表单嵌套,对于这种内嵌的页面 selenium 是没法直接定位的,须要使用 switch_to.frame() 方法将当前操做的对象切换成 frame/iframe 内嵌的页面。

switch_to.frame() 默承认以用的 idname 属性直接定位,但若是 iframe 没有 idname ,这时就须要使用 xpath 进行定位。下面先写一个包含 iframe 的页面作测试用。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div p {
    
    
            color: #red;
            animation: change 2s infinite;
        }
        @keyframes change {
    
    
            from {
    
    
                color: red;
            }
            to {
    
    
                color: blue;
            }
        }
    </style>
</head>

<body>
    <div>
        <p>公众号:Python新视野</p>
        <p>CSDN:Dream丶Killer</p>
        <p>微信:python-sun</p>
    </div>
    <iframe src="https://blog.csdn.net/qq_43965708" width="400" height="200"></iframe>
<!--     <iframe id="CSDN_info" name="Dream丶Killer" src="https://blog.csdn.net/qq_43965708" width="400" height="200"></iframe> -->
</body>
</html>

在这里插入图片描述

如今咱们定位红框中的 CSDN 按钮,能够跳转到 CSDN 首页。

from selenium import webdriver
from pathlib import Path


driver = webdriver.Chrome()
# 读取本地html文件
driver.get('file:///' + str(Path(Path.cwd(), 'iframe测试.html')))

# 1.经过id定位
driver.switch_to.frame('CSDN_info')
# 2.经过name定位
# driver.switch_to.frame('Dream丶Killer')
# 经过xpath定位
# 3.iframe_label = driver.find_element_by_xpath('/html/body/iframe')
# driver.switch_to.frame(iframe_label)

driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/div[1]/div/a/img').click()

这里列举了三种定位方式,均可以定位 iframe
请添加图片描述



弹窗处理

JavaScript 有三种弹窗 alert(确认)、confirm(确认、取消)、prompt(文本框、确认、取消)。

处理方式:先定位(switch_to.alert自动获取当前弹窗),再使用 textacceptdismisssend_keys 等方法进行操做

方法 描述
text 获取弹窗中的文字
accept 接受(确认)弹窗内容
dismiss 解除(取消)弹窗
send_keys 发送文本至警告框

这里写一个简单的测试页面,其中包含三个按钮,分别对应三个弹窗。

<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
    <button id="alert">alert</button>
    <button id="confirm">confirm</button>
    <button id="prompt">prompt</button>

    <script type="text/javascript">
        const dom1 = document.getElementById("alert")
        dom1.addEventListener('click', function(){
    
    
           alert("alert hello")
        })

                const dom2 = document.getElementById("confirm")
        dom2.addEventListener('click', function(){
    
    
            confirm("confirm hello")
        })

                const dom3 = document.getElementById("prompt")
        dom3.addEventListener('click', function(){
    
    
            prompt("prompt hello")
        })
    </script>
</body>
</html>

在这里插入图片描述
下面使用上面的方法进行测试。为了防止弹窗操做过快,每次操做弹窗,都使用 sleep 强制等待一段时间。

from selenium import webdriver
from pathlib import Path
from time import sleep


driver = webdriver.Firefox()
driver.get('file:///' + str(Path(Path.cwd(), '弹窗.html')))

sleep(2)

# 点击alert按钮
driver.find_element_by_xpath('//*[@id="alert"]').click()
sleep(1)
alert = driver.switch_to.alert
# 打印alert弹窗的文本
print(alert.text)
# 确认
alert.accept()

sleep(2)

# 点击confirm按钮
driver.find_element_by_xpath('//*[@id="confirm"]').click()
sleep(1)
confirm = driver.switch_to.alert
print(confirm.text)
# 取消
confirm.dismiss()

sleep(2)

# 点击confirm按钮
driver.find_element_by_xpath('//*[@id="prompt"]').click()
sleep(1)
prompt = driver.switch_to.alert
print(prompt.text)
# 向prompt的输入框中传入文本
prompt.send_keys("Dream丶Killer")
sleep(2)
prompt.accept()

'''输出
alert hello
confirm hello
prompt hello
'''

请添加图片描述

注:细心地读者应该会发现此次操做的浏览器是 Firefox ,为何不用 Chrome 呢?缘由是测试时发现执行 promptsend_keys 时,不能将文本填入输入框。尝试了各类方法并查看源码后确认不是代码的问题,以后经过其余渠道得知缘由多是 Chrome 的版本与 selenium 版本的问题,但也没有很方便的解决方案,所以没有继续深究,改用 Firefox 可成功运行。这里记录一下个人 Chrome 版本,若是有大佬懂得如何在 Chrome 上解决这个问题,请在评论区指导一下,提早感谢!
selenium:3.141.0
Chrome:94.0.4606.71



上传 & 下载文件

上传文件

常见的 web 页面的上传,通常使用 input 标签或是插件(JavaScriptAjax),对于 input 标签的上传,能够直接使用 send_keys(路径) 来进行上传。
先写一个测试用的页面。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="file" name="">
</body>
</html>

在这里插入图片描述
下面经过 xpath 定位 input 标签,而后使用 send_keys(str(file_path) 上传文件。

from selenium import webdriver
from pathlib import Path
from time import sleep


driver = webdriver.Chrome()
file_path = Path(Path.cwd(), '上传下载.html')
driver.get('file:///' + str(file_path))

driver.find_element_by_xpath('//*[@name="upload"]').send_keys(str(file_path))

在这里插入图片描述

下载文件

Chrome浏览器

Firefox 浏览器要想实现文件下载,须要经过 add_experimental_option 添加 prefs 参数。

  • download.default_directory:设置下载路径。
  • profile.default_content_settings.popups:0 禁止弹出窗口。

下面测试下载搜狗图片。指定保存路径为代码所在路径。
在这里插入图片描述

from selenium import webdriver 

prefs = {
   
   'profile.default_content_settings.popups': 0,
         'download.default_directory': str(Path.cwd())}
option = webdriver.ChromeOptions() 
option.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome(options=option)
driver.get("https://pic.sogou.com/d?query=%E7%83%9F%E8%8A%B1&did=4&category_from=copyright")
driver.find_element_by_xpath('/html/body/div/div/div/div[2]/div[1]/div[2]/div[1]/div[2]/a').click()

driver.switch_to.window(driver.window_handles[-1])
driver.find_element_by_xpath('./html').send_keys('thisisunsafe')

代码最后两句猜想有理解什么意思的吗~,哈哈,实际做用是当你弹出像下面的页面 “您的链接不是私密链接” 时,能够直接键盘输入 “thisisunsafe” 直接访问连接。那么这个键盘输入字符串的操做就是之间讲到的 send_keys,但因为该标签页是新打开的,因此要经过 switch_to.window() 将窗口切换到最新的标签页。

请添加图片描述

Firefox浏览器

Firefox 浏览器要想实现文件下载,须要经过 set_preference 设置 FirefoxProfile() 的一些属性。

  • browser.download.foladerList:0 表明按浏览器默认下载路径;2 保存到指定的目录。
  • browser.download.dir:指定下载目录。
  • browser.download.manager.showWhenStarting:是否显示开始,boolean 类型。
  • browser.helperApps.neverAsk.saveToDisk:对指定文件类型再也不弹出框进行询问。
  • HTTP Content-type对照表https://www.runoob.com/http/http-content-type.html
from selenium import webdriver
import os
fp = webdriver.FirefoxProfile()
fp.set_preference("browser.download.dir",os.getcwd())
fp.set_preference("browser.download.folderList",2)
fp.set_preference("browser.download.manager.showhenStarting",True)
fp.set_preference("browser.helperApps.neverAsk.saveToDisk","application/octet-stream")

driver = webdriver.Firefox(firefox_profile = fp)
driver.get("https://pic.sogou.com/d?query=%E7%83%9F%E8%8A%B1&did=4&category_from=copyright")
driver.find_element_by_xpath('/html/body/div/div/div/div[2]/div[1]/div[2]/div[1]/div[2]/a').click()

运行效果与 Chrome 基本一致,这里就再也不展现了。



cookies操做

cookies 是识别用户登陆与否的关键,爬虫中经常使用 selenium + requests 实现 cookie持久化,即先用 selenium 模拟登录获取 cookie ,再经过 requests 携带 cookie 进行请求。

webdriver 提供 cookies 的几种操做:读取、添加删除。

  • get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
  • get_cookie(name):返回 cookie 字典中 key == namecookie 信息。
  • add_cookie(cookie_dict):将 cookie 添加到当前会话中
  • delete_cookie(name):删除指定名称的单个 cookie
  • delete_all_cookies():删除会话范围内的全部 cookie

下面看一下简单的示例,演示了它们的用法。

from selenium import webdriver 

driver = webdriver.Chrome()
driver.get("https://blog.csdn.net/")

# 输出全部cookie信息
print(driver.get_cookies())

cookie_dict = {
   
   
    'domain': '.csdn.net',
     'expiry': 1664765502,
     'httpOnly': False,
     'name': 'test',
     'path': '/',
     'secure': True,
     'value': 'null'}

# 添加cookie
driver.add_cookie(cookie_dict)
# 显示 name = 'test' 的cookie信息
print(driver.get_cookie('test'))
# 删除 name = 'test' 的cookie信息
driver.delete_cookie('test')
# 删除当前会话中的全部cookie
driver.delete_all_cookies()

在这里插入图片描述
在这里插入图片描述



调用JavaScript

webdriver 对于滚动条的处理须要用到 JavaScript ,同时也能够向 textarea 文本框中输入文本( webdriver 只能定位,不能输入文本),webdriver 中使用execute_script方法实现 JavaScript 的执行。

滑动滚动条

经过 x ,y 坐标滑动

对于这种经过坐标滑动的方法,咱们须要知道作表的起始位置在页面左上角(0,0),下面看一下示例,滑动 CSDN 首页。

from selenium import webdriver 
from time import sleep

driver = webdriver.Chrome()
driver.get("https://blog.csdn.net/")
sleep(1)

js = "window.scrollTo(0,500);"
driver.execute_script(js)

经过参照标签滑动

这种方式须要先找一个参照标签,而后将滚动条滑动至该标签的位置。下面仍是用 CSDN 首页作示例,咱们用循环来实现重复滑动。该 li 标签实际是一种懒加载,当用户滑动至最后标签时,才会加载后面的数据。

from selenium import webdriver 
from time import sleep

driver = webdriver.Chrome()
driver.get("https://blog.csdn.net/")
sleep(1)
driver.implicitly_wait(3)

for i in range(31, 102, 10):
    sleep(1)
    target = driver.find_element_by_xpath(f'//*[@id="feedlist_id"]/li[{i}]')
    driver.execute_script("arguments[0].scrollIntoView();", target)

请添加图片描述



其余操做

关闭全部页面

使用 quit() 方法能够关闭全部窗口并退出驱动程序。

driver.quit()

关闭当前页面

使用 close() 方法能够关闭当前页面,使用时要注意 “当前页面” 这四个字,当你关闭新打开的页面时,须要切换窗口才能操做新窗口并将它关闭。,下面看一个简单的例子,这里不切换窗口,看一下是否可以关闭新打开的页面。

from selenium import webdriver
from time import sleep

driver = webdriver.Chrome()
driver.get('https://blog.csdn.net/')
driver.implicitly_wait(3)

# 点击进入新页面
driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click()
# 切换窗口
# driver.switch_to.window(driver.window_handles[-1])

sleep(3)
driver.close()

请添加图片描述
能够看到,在不切换窗口时,driver 对象仍是操做最开始的页面。

对当前页面进行截图

wendriver 中使用 get_screenshot_as_file() 对 “当前页面” 进行截图,这里和上面的 close() 方法同样,对于新窗口的操做,必定要切换窗口,否则截的仍是原页面的图。对页面截图这一功能,主要用在咱们测试时记录报错页面的,咱们能够将 try except 结合 get_screenshot_as_file() 一块儿使用来实现这一效果。

try:
    driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click()
except:
    driver.get_screenshot_as_file(r'C:\Users\pc\Desktop\screenshot.png')

经常使用方法总结

# 获取当前页面url
driver.current_url

# 获取当前html源码
driver.page_source

# 获取当前页面标题
driver.title

# 获取浏览器名称(chrome)
driver.name

# 对页面进行截图,返回二进制数据
driver.get_screenshot_as_png()

# 设置浏览器尺寸
driver.get_window_size()

# 获取浏览器尺寸,位置
driver.get_window_rect()

# 获取浏览器位置(左上角)
driver.get_window_position()

# 设置浏览器尺寸
driver.set_window_size(width=1000, height=600)

# 设置浏览器位置(左上角)
driver.set_window_position(x=500, y=600)

# 设置浏览器的尺寸,位置
driver.set_window_rect(x=200, y=400, width=1000, height=600)


selenium进阶

selenium隐藏指纹特征

selenium 对于部分网站来讲十分强大,但它也不是万能的,实际上,selenium 启动的浏览器,有几十个特征能够被网站检测到,轻松的识别出你是爬虫。
不相信?接着往下看,首先你手动打开浏览器输入https://bot.sannysoft.com/,在网络无异常的状况下,显示应该以下:
在这里插入图片描述
下面经过 selenium 来打开浏览器。

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://bot.sannysoft.com/')

在这里插入图片描述
经过 webdriver:present 能够看到浏览器已经识别出了你是爬虫,咱们再试一下无头浏览器。

from selenium import webdriver

# 设置无头浏览器
option = webdriver.ChromeOptions()
option.add_argument('--headless')

driver = webdriver.Chrome()

driver.get('https://bot.sannysoft.com/')
# 对当前页面进行截图
driver.save_screenshot('page.png')

请添加图片描述
没错,就是这么真实,对于常规网站可能没什么反爬,但真正想要抓你仍是一抓一个准的。

说了这么多,是否是 selenium 真的不行?别着急,实际仍是解决方法的。关键点在于如何在浏览器检测以前将这些特征进行隐藏,事实上,前人已经为咱们铺好了路,解决这个问题的关键,实际就是一个 stealth.min.js 文件,这个文件是给 puppeteer 用的,在 Python 中使用的话须要单独执行这个文件,该文件获取方式须要安装 node.js ,若是已安装的读者能够直接运行以下命令便可在当前目录生成该文件。

npx extract-stealth-evasions

这里我已经成功获取了 stealth.min.js 文件。
连接:https://pan.baidu.com/s/1O6co1Exa8eks6QmKAst91g
提取码:关注文末小卡片回复“隐藏指纹特征”获取

下面咱们在网站检测以前先执行该js文件隐藏特征,一样使用无头浏览器,看是否有效。

import time
from selenium.webdriver import Chrome

option = webdriver.ChromeOptions()
option.add_argument("--headless")
# 无头浏览器须要添加user-agent来隐藏特征
option.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36')

driver = Chrome(options=option)
driver.implicitly_wait(5)

with open('stealth.min.js') as f:
    js = f.read()

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
   
   
  "source": js
})

driver.get('https://bot.sannysoft.com/')
driver.save_screenshot('hidden_features.png')

请添加图片描述
经过 stealth.min.js 的隐藏,能够看到此次使用无头浏览器特征基本都以隐藏,已经十分接近人工打开浏览器了。



实战:selenium模拟登陆B站

登陆验证码处理

selenium 中的难点验证码破解在上文中并无说起,由于确实没有很好的方式,通常都须要经过第三方平台实现破解,本案例中使用的是超级鹰平台(收费,大概1元30次,测试用冲个1元就足够)。下面实战开始!

分析登陆界面结构

B站登陆界面以下。
在这里插入图片描述

首先明确咱们的目标,打开登录界面,定位用户名和密码对应的标签,输入相关数据后,点击登陆,此时页面会弹出文字验证码。
在这里插入图片描述

下文会用两种方法进行验证码图片的获取,并提交给超级鹰进行识别,接收到汉字的坐标后,处理坐标数据,而后用动做链点击对应坐标操做,完成登陆。

下面使用 selenium 打开登陆页面。

driver.get('https://passport.bilibili.com/login')
# 定位用户名,密码输入框
username = driver.find_element_by_id('login-username')
password = driver.find_element_by_id('login-passwd')
# 将本身的用户名密码替换xxxxxx
username.send_keys('xxxxxx')
password.send_keys('xxxxxx')

# 定位登陆按钮并点击
driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()

获取页面当前验证码图片

方法1、页面截图,将验证码区域进行裁剪保存

使用此方法时,注意咱们截取验证码图片时须要截取完整,不要只截图片部分,上面文字也须要。完整验证码截图以下:

在这里插入图片描述

首先将点击登陆后的页面进行截图,而后定位到验证码的位置,经过location()方法获取验证码左上角的坐标, size() 获取验证码的宽和高,左上角坐标加上宽和高就是验证码右下角的坐标。获取坐标后就能够用**crop()**方法来进行裁剪,而后将裁剪到的验证码图片保存。

此时虽然获取了验证码图片,可是还不能直接提交给超级鹰。
由于超级鹰识别的验证码图片的宽和高有限制,最好不超过 460px,310px
可是截取到的验证码图片宽高为 338px,432px,这时就要先将图片缩小一倍再提交便可,等到收到坐标数据再将坐标乘2

def save_img():
    # 对当前页面进行截图保存
    driver.save_screenshot('page.png')
    # 定位验证码图片的位置
    code_img_ele = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div')
    # 获取验证码左上角的坐标x,y
    location = code_img_ele.location
    # 获取验证码图片对应的长和宽
    size = code_img_ele.size

    # 左上角和右下角的坐标
    rangle = (
        int(location['x'] * 1.25), int(location['y'] * 1.25), int((location['x'] + size['width']) * 1.25),
        int((location['y'] + size['height']) * 1.25)
    )

    i = Image.open('./page.png')
    code_img_name = './code.png'
    # crop根据rangle元组内的坐标进行裁剪
    frame = i.crop(rangle)
    frame.save(code_img_name)
    return code_img_ele

def narrow_img():
    # 缩小图片
    code = Image.open('./code.png')
    small_img = code.resize((169, 216))
    small_img.save('./small_img.png')
    print(code.size, small_img.size)

方法2、经过网页获取图片地址,并保存

这种方法比上一种更加方便,分析网页源码获取图片地址,对该地址发送请求,接收返回的二进制文件,进行保存。首先打开网页源码找到图片地址。

请添加图片描述

图片地址是 img 标签的 src 属性值,经过 xpath 获得地址,直接对此 url 发送请求,接收数据并保存便可。
注意:因为获取的图片的高度仍然大于超级鹰标准格式,因此也须要将图片缩小。

# 获取img标签的src属性值
img_url = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[2]/div[1]/div/div[2]/img').get_attribute('src')
headers = {
   
   
    'Users-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36'
}
# 获取图片二进制数据
img_data = requests.get(url=img_url, headers=headers).content
with open('./node1.png', 'wb')as fp:
    fp.write(img_data)
i = Image.open('./node1.png')
# 将图片缩小并保存,设置宽为172,高为192
small_img = i.resize((172, 192))
small_img.save('./small_img1.png')

使用超级鹰识别验证码

这部分没什么说的,直接调用就行。

# 将验证码提交给超级鹰进行识别
chaojiying = Chaojiying_Client('用户名', '密码', '96001')  # 用户中心>>软件ID 生成一个替换 96001
im = open('small_img.png', 'rb').read()  # 本地图片文件路径 来替换 a.jpg 有时WIN系统需要//
# 9004是验证码类型
print(chaojiying.PostPic(im, 9004)['pic_str'])
result = chaojiying.PostPic(im, 9004)['pic_str']

提取坐标数据,动做链点击

超级鹰识别返回的数据格式是:123,12 | 234,21 。咱们能够将数据以 ' | ' 进行分割,保存到列表中,再以逗号分割将 x,y 的坐标保存,获得 [ [123,12],[234,21] ] 这一格式,而后遍历这一列表,使用动做链对每个列表元素对应的 x,y 指定的位置进行点击操做,最后定位并点击确认,登陆成功。

all_list = []  # 要存储即将被点击的点的坐标  [[x1,y1],[x2,y2]]
if '|' in result:
    list_1 = result.split('|')
    count_1 = len(list_1)
    for i in range(count_1):
        xy_list = []
        x = int(list_1[i].split(',')[0])
        y = int(list_1[i].split(',')[1])
        xy_list.append(x)
        xy_list.append(y)
        all_list.append(xy_list)
else:
    x = int(result.split(',')[0])
    y = int(result.split(',')[1])
    xy_list = []
    xy_list.append(x)
    xy_list.append(y)
    all_list.append(xy_list)

# 遍历列表,使用动做链对每个列表元素对应的x,y指定的位置进行点击操做
# x,y坐标乘2和0.8,是因为以前图片缩放过,因此*2,0.8是由于本人电脑桌面缩放比例为125%,须要还原成1
for l in all_list:
    x = l[0] * 2 * 0.8
    y = l[1] * 2 * 0.8
    # 将点击操做的参照物移动到指定的模块,
    # 若用方法二获取的验证码图片,要添加下面代码对code_img_ele赋值
    # code_img_ele = bro.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[2]/div[1]/div/div[2]/img')
    ActionChains(driver).move_to_element_with_offset(code_img_ele, x, y).click().perform()
    print('点击已完成')

# 完成动做链点击操做后,定位确认按钮并点击
driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[3]/a').click()

运行效果

因为验证码处理须要用到第三方平台,外加设置了强制等待,总体运行速度较慢。
请添加图片描述



❤️源码获取❤️

对于刚入门 Python 或是想要入门 Python 的小伙伴,能够经过下方公众号联系做者 进入交流群,一块儿交流学习,都是重新手走过来的,有时候一个简单的问题卡好久,但可能别人的一点拨就会恍然大悟,由衷的但愿你们可以共同进步。另有整理的近千套简历模板,几百册电子书等你来领取哦!

也能够直接加本人微信,备注:【交流群】,我拉你进Python交流群👇🏻

👇🏻 实战源码 可关注最下方 公众号卡片 回复 模拟登陆B站 获取,回复“666”入群👇🏻