2023年11月25日发(作者:)

python-selenium库详解

python-selenium库详解

Selenium 是⼀个⾃动化测试⼯具,利⽤它可以驱动浏览器执⾏特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页⾯

的源代码,做到可见即可爬。对于⼀些 JavaScript 动态渲染的页⾯来说,此种抓取⽅式⾮常有效。

um库的安装

pip3 install selenium

um库的基本⽤法

1)声明浏览器对象

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()

这样就完成了浏览器对象的初始化并将其赋值为 browser 对象。

2)访问页⾯

可以⽤ get() ⽅法来请求⽹页,参数传⼊链接 URL 即可

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('')

print(browser.page_source)

browser.close()

运⾏后发现,弹出了 Chrome 浏览器并且⾃动访问了淘宝,然后控制台输出了淘宝页⾯的源代码,随后浏览器关闭。

3)查找结点

想要从淘宝页⾯中提取搜索框这个节点,可以发现,它的 id 是 q,name 也是 q

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('')

input_first = browser.find_element_by_id('q')

input_second = browser.find_element_by_css_selector('#q')

input_third = browser.find_element_by_xpath('//*[@id="q"]')

print(input_first, input_second, input_third)

browser.close()

运⾏结果

所有获取单个节点的⽅法:

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

另外,Selenium 还提供了通⽤⽅法 find_element(),它需要传⼊两个参数:查找⽅式 By 和值。实际上,它就是 find_element_by_id()

这种⽅法的通⽤函数版本,⽐如 find_element_by_id(id) 就等价于 find_element(, id),⼆者得到的结果完全⼀致。

from selenium import webdriver

from selenium.webdriver.common.by import By

browser = webdriver.Chrome()

browser.get('')

input_first = browser.find_element(By.ID, 'q')

print(input_first)

browser.close()

多个节点

如果查找的⽬标在⽹页中只有⼀个,那么完全可以⽤ find_element() ⽅法。如果要查找所有满⾜条件的节点,需要⽤

find_elements()

样的⽅法。

注意,在这个⽅法的名称中,element 多了⼀个 s。

要查找淘宝左侧导航条的所有条⽬

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('')

lis = browser.find_elements_by_css_selector('.service-bd li')

print(lis)

browser.close()

所有获取多个节点的⽅法:

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')

4)节点交互

Selenium 可以驱动浏览器来执⾏⼀些操作,也就是说可以让浏览器模拟执⾏⼀些动作。⽐较常见的⽤法有:输⼊⽂字时⽤ send_keys ⽅

法,清空⽂字时⽤ clear ⽅法,点击按钮时⽤ click ⽅法。

from selenium import webdriver

import time

browser = webdriver.Chrome()

browser.get('')

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()

这⾥⾸先驱动浏览器打开淘宝,然后⽤ find_element_by_id() ⽅法获取输⼊框,然后⽤ send_keys() ⽅法输⼊ iPhone ⽂字,等待⼀秒后

⽤ clear() ⽅法清空输⼊框,再次调⽤ send_keys() ⽅法输⼊ iPad ⽂字,之后再⽤ find_element_by_class_name() ⽅法获取搜索按

钮,最后调⽤ click() ⽅法完成搜索动作。

5)动作链

⼀些交互动作都是针对某个节点执⾏的。⽐如,对于输⼊框,我们就调⽤它的输⼊⽂字和清空⽂字⽅法;对于按钮,就调⽤它的点击⽅法。

还有另外⼀些操作,它们没有特定的执⾏对象,⽐如⿏标拖曳、键盘按键等,这些动作⽤另⼀种⽅式来执⾏,那就是动作链。

from selenium import webdriver

from selenium.webdriver import ActionChains

browser = webdriver.Chrome()

url = '/try/?filename=jqueryui-api-droppable'

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()

打开⽹页中的⼀个拖曳实例,然后依次选中要拖曳的节点和拖曳到的⽬标节点,接着声明 ActionChains 对象并将其赋值为 actions 变

量,然后通过调⽤ actions 变量的 drag_and_drop() ⽅法,再调⽤ perform() ⽅法执⾏动作,此时就完成了拖曳操作

6)执⾏ JavaScript

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('/explore')

browser.execute_script('To(0, Height)')

browser.execute_script('alert("To Bottom")')

这⾥就利⽤ execute_script() ⽅法将进度条下拉到最底部,然后弹出 alert 提⽰框。

3.获取节点信息

通过 page_source 属性可以获取⽹页的源代码,接着就可以使⽤解析库(如正则表达式、Beautiful Soup、pyquery 等)来提取信息。

1)获取属性

我们可以使⽤ get_attribute() ⽅法来获取节点的属性,但是其前提是先选中这个节点。

from selenium import webdriver

from selenium.webdriver import ActionChains

browser = webdriver.Chrome()

url = '/explore'

browser.get(url)

logo = browser.find_element_by_id('zh-top-link-logo')

print(logo)

print(logo.get_attribute('class'))

运⾏之后,程序便会驱动浏览器打开知乎页⾯,然后获取知乎的 logo 节点,最后打印出它的 class。

2)获取⽂本值

每个 WebElement 节点都有 text 属性,直接调⽤这个属性就可以得到节点内部的⽂本信息,这相当于 Beautiful Soup 的 get_text() ⽅

法、pyquery 的 text() ⽅法。

from selenium import webdriver

browser = webdriver.Chrome()

url = '/explore'

browser.get(url)

input = browser.find_element_by_class_name('ExploreSpecialCard-title')

print(input.text)

运⾏结果

科⽐意外离世,你对他有哪些回忆?

3)获取 ID、位置、标签名、⼤⼩

from selenium import webdriver

browser = webdriver.Chrome()

url = '/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)

这⾥⾸先获得 “提问” 按钮这个节点,然后调⽤其 id、location、tag_name、size 属性来获取对应的属性值。

4.切换 Frame

⽹页中有⼀种节点叫作 iframe,也就是⼦ Frame,相当于页⾯的⼦页⾯,它的结构和外部⽹页的结构完全⼀致。Selenium 打开页⾯后,

它默认是在⽗级 Frame ⾥⾯操作,⽽此时如果页⾯中还有⼦ Frame,它是不能获取到⼦ Frame ⾥⾯的节点的。这时就需要使⽤

switch_() ⽅法来切换 Frame

5.前进后退

import time

from selenium import webdriver

from selenium.common.exceptions import NoSuchElementException

browser = webdriver.Chrome()

url = '/try/?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_() ⽅法切换到⼦ Frame ⾥⾯,然后尝试获取⼦ Frame ⾥的 logo 节点(这是不能找到的),如果找不到的

话,就会抛出 NoSuchElementException 异常,异常被捕捉之后,就会输出 NO LOGO。接下来,重新切换回⽗级 Frame,然后再次重

新获取节点,发现此时可以成功获取了。

所以,当页⾯中包含⼦ Frame 时,如果想获取⼦ Frame 中的节点,需要先调⽤ switch_() ⽅法切换到对应的 Frame,然后再进

⾏操作。

6.延时等待

在 Selenium 中,get() ⽅法会在⽹页框架加载结束后结束执⾏,此时如果获取 page_source,可能并不是浏览器完全加载完成的页⾯,

如果某些页⾯有额外的 Ajax 请求,我们在⽹页源代码中也不⼀定能成功获取到。所以,这⾥需要延时等待⼀定时间,确保节点已经加载出

来。

这⾥等待的⽅式有两种:

隐式等待

当使⽤隐式等待执⾏测试的时候,如果 Selenium 没有在 DOM 中找到节点,将继续等待,超出设定时间后,则抛出找不到节点的异

常。换句话说,当查找节点⽽节点并没有⽴即出现的时候,隐式等待将等待⼀段时间再查找 DOM,默认的时间是 0。

from selenium import webdriver

browser = webdriver.Chrome()

browser.implicitly_wait(10)

browser.get('/explore')

input = browser.find_element_by_class_name('zu-top-add-question')

print(input)

在这⾥我们⽤ implicitly_wait() ⽅法实现了隐式等待。

显式等待

隐式等待的效果其实并没有那么好,因为我们只规定了⼀个固定时间,⽽页⾯的加载时间会受到⽹络条件的影响。

这⾥还有⼀种更合适的显式等待⽅法,它指定要查找的节点,然后指定⼀个最长等待时间。如果在规定时间内加载出来了这个节点,就

返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常。

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

browser = webdriver.Chrome()

browser.get('/')

wait = WebDriverWait(browser, 10)

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 节点加载出,传⼊定位元组,如 (, ‘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

更多详细的等待条件的参数及⽤法介绍可以参考官⽅⽂档:

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('/explore')

print(browser.get_cookies())

browser.add_cookie({'name': 'name', 'domain': '', '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。再重新获取,发现结果就为空了。

8.选项卡管理

import time

from selenium import webdriver

browser = webdriver.Chrome()

browser.get('')

browser.execute_script('()')

print(browser.window_handles)

browser.switch_to_window(browser.window_handles[1])

browser.get('')

time.sleep(1)

browser.switch_to_window(browser.window_handles[0])

browser.get('')

⾸先访问了百度,然后调⽤了 execute_script() ⽅法,这⾥传⼊ () 这个 JavaScript 语句新开启⼀个选项卡。接下来,我们

想切换到该选项卡。这⾥调⽤ window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要

调⽤ switch_to_window() ⽅法即可,其中参数是选项卡的代号。这⾥我们将第⼆个选项卡代号传⼊,即跳转到第⼆个选项卡,接下来在第

⼆个选项卡下打开⼀个新页⾯,然后切换回第⼀个选项卡重新调⽤ switch_to_window() ⽅法,再执⾏其他操作即可。

9.异常处理

from selenium import webdriver

from selenium.common.exceptions import TimeoutException, NoSuchElementException

browser = webdriver.Chrome()

try:

browser.get('')

except TimeoutException:

print('Time Out')

try:

browser.find_element_by_id('hello')

except NoSuchElementException:

print('No Element')

finally:

browser.close()

这⾥我们使⽤ try except 来捕获各类异常。⽐如,我们对 find_element_by_id() 查找节点的⽅法捕获 NoSuchElementException 异

常,这样⼀旦出现这样的错误,就进⾏异常处理,程序也不会中断了。