在之前,我们了解了 Ajax 的分析和抓取方式,这其实也是 JavaScript 动态渲染的页面的一种情形,通过直接分析 Ajax ,我们仍然可以借助 requests 或 urllib 来实现数据爬取。
不过,JavaScript 渲染的页面不止 Ajax 一种,为了解决这些问题,我们可以直接使用模拟浏览器运行的方式来实现。这样就可以做到在浏览器中看到是什么样,抓取到的源码就是什么样,也就是可见即可爬。这样我们就不用再去管网页内部的 JavaScript 用了什么算法渲染页面,不用管网页后台的 Ajax 接口到底有哪些参数。
Selenium 的使用
Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码 ,做到可见即可爬。对于一些 JavaScript 动态渲染的页面来说,此种抓取方式非常有效。
准备工作
此处以 Chrome 为例来讲解 Selenium 的用法。在开始之前,请确保已经正确安装好了 Chrome 览器并配置好了 ChromeDriver(要和浏览器版本相匹配)。 另外,还需要正确安装好 Python 的 Selenium 库。
基本使用
首先来大体看一下 Selenium 有一些怎么样的功能。示例如下:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()
try:
driver.get('https://www.baidu.com')
driver.find_element_by_id('kw').send_keys('python')
driver.find_element_by_id('kw').send_keys(Keys.ENTER)
driver.implicitly_wait(10)
driver.find_element_by_id('content_left')
print(driver.current_url)
finally:
print('运行结束')
运行代码后发现,会自动弹出一个 Chrome 浏览器。浏览器首先会跳转到百度,然后在搜索框中输入 python ,接着跳转到搜索结果页面,如下图:
而且,在搜索结果加载出来之后,控制台会输入当前的 URL :
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=python&fenlei=256&rsv_pq=cd74056900082e02&rsv_t=8733EHnEPYv6rv1oFX3M4Q4VzZGOIlqMYfTas7e4CerS4RszUwAbnKPK8F8&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=6&rsv_sug2=0&rsv_btype=i&inputT=92&rsv_sug4=92
可以看到,我们得到的当前 URL 是浏览器中的真实内容。
所以说,如果用 Selenium 来驱动浏览器加载网页的话 就可以直接拿到 JavaScript 渲染的结果了。
下面来详细了解一下 Selenium 的用法。
声明浏览器对象
Selenium 支持非常多的浏览器,如Chrome、firefox、Edge等,还有 Android 等手机端的浏览器。此外,也支持无界面的浏览器 PhantomJS。
我们可以使用如下方式初始化:
from selenium import webdriver
driver = webdriver.Chrome()
driver = webdriver.Firefox()
driver = webdriver.Edge()
driver = webdriver.PhantomJS()
driver = webdriver.Safari()
这样就完成了浏览器对象的初始化并将其赋值为 driver 对象。接下来,我们就可以调用 driver 对象,让其执行各种动作以模拟浏览器操作。
访问页面
我们在这里使用 get() 方法来请求网页,参数传入链接 URL 即可。比如,这里用 get() 方法访问淘宝,然后打印出其源代码,代码如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://ww.taobao.com')
print(driver.page_source)
# 关闭页面
driver.close()
运行后发现,弹出了 Chrome 浏览器并且自动访问了淘宝,然后在控制台输出了淘宝页面的源代码。
通过这几行简单的代码,我们可以实现浏览器的驱动并 取网页源码,非常便捷。
查找节点
Selenium 可以驱动浏览器完成各种操作,比如填充表单、模拟点击等。如果我们想要完成向某个输入框输入文字的操作,我们首先要知道这个输入框在什么位置。Selenium 提供了一系列查找节点的方法,我们可以用这些方法来获取想要的节点,以便下一步执行一些动作或者提取信息。
单个节点
比如,想要从淘宝页面中提取搜索框这个节点,首先要观察它的源代码。
可以发现,搜索框的id是q,name也是q。此外,还有其他的一些属性,此时,我们就可以利用多种方式去获取到搜索框的位置。下面用代码实现一下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.taobao.com')
driver.implicitly_wait(10)
input_first = driver.find_element_by_id('q')
input_second = driver.find_element_by_css_selector('#q')
input_third = driver.find_element_by_xpath('//*[@id="q"]')
print(input_first,input_second,input_third)
driver.close()
这里我们使用3种方式获取输入框,分别是根据id、css选择器和xpath获取,它们返回的结果完全一致。运行结果如下:
<selenium.webdriver.remote.webelement.WebElement (session="81e937bd833f62ed0bd8cce8893fa76b", element="466d3924-2778-4df9-a40f-e93ca3de7e47")>
<selenium.webdriver.remote.webelement.WebElement (session="81e937bd833f62ed0bd8cce8893fa76b", element="466d3924-2778-4df9-a40f-e93ca3de7e47")>
<selenium.webdriver.remote.webelement.WebElement (session="81e937bd833f62ed0bd8cce8893fa76b", element="466d3924-2778-4df9-a40f-e93ca3de7e47")>
可以看到,这三个节点都是WebElement 类型,是完全一致的。
此外,还有以下获取单个节点的方法:
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_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(By.ID, id),二者得到的结果完全一致。代码示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.taobao.com')
driver.implicitly_wait(10)
input_first = driver.find_element(By.ID,'q')
print(input_first)
driver.close()
实际上,这种查找方式的功能和上面的查找函数完全一致,不过参数更加灵活。
多个节点
如果查找的目标在网页中只有一个,那么完全可以用 find_element() 方法。但如果有多个节点,再用 find_e lement() 方法查找,就只能得到第一个节点了。如果要查找所有满足条件的节点, 需要用 find_elements() 这样的方法。注意,在这个方法的名称中, element 多了一个 s,注意区分。
比如,要查找淘宝左侧导航条的所有条目。
代码如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://ww.taobao.com')
lis = driver.get_elements_by_class_name('J_Cat')
print(lis)
运行结果如下:
[<selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="c919812b-92ee-436c-a801-cc1ba47e4380")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="f9de96f1-c684-4843-91ff-f6bbbb826147")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="be8b8d40-1108-45be-a894-246bc0690e65")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="40c06367-2b66-40ab-873e-5b60fd512f14")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="45c51c9d-e299-4d15-acf5-1483a4d11136")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="b2f24cf5-4280-48b1-ab6b-853bdef6dd14")>, <selenium.webdriver.remote.webelement.WebElement (session="4162f334ab2d3978937e91dbae4ac7e7", element="7bd6be3f-30a0-4420-885d-0e70fd9bcbef")>]
这里简化了输出结果,中间部分省略。
可以看到,得到的结果变成了列表类型,列表中的每一个元素都是 WebElement 类型。
当然,我们也可以直接用 find_elements() 方法来选择,这时可以这样写:
lis = driver.find_elements(By.CLASS_NAME,'J_Cat')
结果是完全一致的。
节点交互
Selenium 可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。比较常见的用法有:输入文字时用 send_keys() 方法,清空文字时用 clear() 方法,点击按钮时用 click() 方法。示例如下:
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.taobao.com')
input = driver.find_element_by_id('q')
input.send_keys('iphone')
time.sleep(2)
input.clear()
input.send_keys('ipad')
button = driver.find_element_by_class_name('btn-search')
button.click()
这里首先驱动浏览器打开淘宝,然后用 find_element_by_id() 方法获取输入框,然后用 send_keys() 方法输入 iphone 文字,等待两秒后用 clear() 方法获取搜索按钮,最后调用 click() 方法完成搜索动作。
通过上面的方法,我们就完成了一些常见节点的动作操作,更多的操作可以参见官方文档的交互动作介绍:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。
动作链
在上面的实例中,一些交互动作都是针对某个节点执行的。比如,对于输入框,我们就调用它的输入文字和清空文字的方法;对于按钮,我们就调用它的点击方法。其实还有一些操作,它们没有特定的执行对象,比如鼠标拖拽、键盘按键等,这些动作用另一种方式执行,那就是行为链。
比如,现在实现一个节点的拖拽操作,将某个节点从一处拖拽到另外一处,可以这样实现:
from selenium import webdriver
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
driver.get(url)
driver.switch_to.frame('iframeResult')
source = driver.find_element_by_css_selector('#draggable')
target = driver.find_element_by_css_selector('#droppable')
actions = ActionChains(driver)
actions.drag_and_drop(source,target)
actions.perform()
首先,打开网页中的一个拖拽实例,然后依次选中要拖拽的节点和目标节点,接着声明行为链对象并将其赋值为 actions ,然后通过调用 actions 变量的 drag_and_drop() 方法,最后调用 perform() 方法执行动作,此时就完成了拖拽操作。如下图:
执行 JavaScript
对于某些操作,Selenium API 并没有提供。比如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script() 方法即可实现,代码如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.zhihu.com/explore')
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
driver.execute_script('alert("To Buttom")')
这里就利用 execute_script() 方法将进度条下拉到最底部,然后弹出 alert 提示框。
有了这个方法,基本上 API 没有提供的所有功能都可以用执行 JavaScript 的方式来实现了。
获取节点信息
前面说过,通过 page_source 属性可以获取页面的源代码,接着就可以使用解析库(如正则表达式,Beautiful Soup、Xpath等)来提取信息了。
不过既然 selenium 已经提供了选择节点的方法,返回的是 WebElement 类型,那么它也有相关的方法和属性来直接提取节点信息,如属性、文本等。这样的话我们就不用通过解析源代码来提取信息了,非常方便。
获取属性
我们可以使用 get_attribute() 方法来获取节点的属性,但是前提是先选中这个节点,示例如下:
from selenium import webdrive
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
driver.get(url)
logo = driver.find_element_by_id('special')
print(logo)
print(logo.get_attribute('class'))
运行之后,程序便会驱动浏览器打开知乎页面,然后获取id值为special的节点,最后打印出它的class。
控制台的输出结果为:
<selenium.webdriver.remote.webelement.WebElement (session="5852f7289eb2c920a8b2404bd5b61260", element="ed8b4406-35ff-45eb-85bf-761606dca5a4")>
ExploreHomePage-ContentSection ExploreHomePage-section
通过 get_attribute() 方法,然后传入想要获取的属性名,就可以得到它的值了。
获取文本值
每一个 WebElement 节点都有 text 属性,直接调用这个属性就可以获取到节点内部的文本信息了,这相当于 Beautiful Soup 的 get_text() 方法,示例如下:
from selenium import webdriver
driver = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
driver.get(url)
input = driver.find_element_by_class_name('ExploreHomePage-ContentSection-header')
print(input.text)
在这里,我们获取“最新专题”这个节点,然后将其文本值打印出来。
输出结果为:
最新专题
获取id、位置、标签名和大小
另外, WebElement 节点还有一些其他属性,比如 id 属性可以获取节点 id(没有id会自动生成),location 属性可以获取该节点在页面中的相对位置, tag_name 属性可以获取标签名称, size 属性可以获取节点的大小,也就是宽高,这些属性有时候还是很有用的。示例如下:
from selenium import webdriver
driver = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
driver.get(url)
input = driver.find_element_by_class_name('ExploreHomePage-ContentSection-header')
print(input.id)
print(input.loaction)
print(input.tag_name)
print(input.size)
运行结果如下:
b3b2996c-005e-4eef-8f0b-aa3cb8537529
{'x': 16, 'y': 84}
div
{'height': 36, 'width': 1000}
切换 Frame
我们知道网页中有一种节点叫作 iframe ,也就是子 Frame ,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium 打开页面后,它默认是在父级 Frame 里面操作,而此时如果页面中还有子 Frame ,它是不能获取到子 Frame 里面的节点的。这时就需要使用 switch_to.frame() 方法来切换 Frame。示例如下:
import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
driver.get(url)
driver.switch_to.frame('iframeResult')
try:
logo = driver.find_element_by_class_name('logo')
except NoSuchElementException:
print('NO LOGO')
driver.switch_to.parent_frame()
logo = driver.find_element_by_class_name('logo')
print(logo)
print(logo.text)
运行结果如下:
NO LOGO
<selenium.webdriver.remote.webelement.WebElement (session="d5473ca19a98e182b55101d1c4725a2b", element="89420a7a-c3b9-4cbe-b7d8-5036fd6e04ee")>
RUNOOB.COM
这里还是以前面演示行为链操作的网页为例,首先通过 switch_to.frame() 方法切换到子 Frame 里面,然后尝试获取父级 Frame 里的 logo 节点(这是不能找到的),找不到就会抛出 NoSuchElementException 异常,捕获异常后就会输出 NO LOGO。接下来,通过 switch_to.parent_frame() 切换回父级 Frame,然后再次获取节点,此时,可以成功获取。
所以,当页面中包含子 Frame 时,如果想获取子 Frame 中的节点,需要先调用 switch_to.frame() 方法进行相应的切换,然后再进行操作。
延时等待
在 Selenium 中,get() 方法会在网页框架加载结束后结束执行,此时如果获取 page_source,可能并不是浏览器完全加载完成的页面,如果有些页面有额外的 Ajax 请求,我们在网页源代码中也不一定能成功获取到。所以,这里需要延迟等待一些时间,确保节点已经加载出来。
这里等待的方式有三种,第一种是强制等待,第二种是隐式等待,第三种是显示等待。
强制等待
利用 Python 自带的 time 模块进行强制时间等待,不管什么情况都会进行等待。示例如下:
import time
from selenium import webdriver
driver = webdriver.Chrome()
time.sleep(5)
driver.get('https://www.taobao.com')
隐式等待
在使用隐式等待时,如果 Selenium 没有在 DOM 中找到节点,将继续等待,超出设定的时间后,则抛出找不到节点的异常。换句话说,当查找节点而节点并没有立即出现的时候,隐式等待将等待一段时间再查找 DOM ,默认时间是 0。示例如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.zhihu.com/explore')
driver.implicitly_wait(10)
input = driver.find_element_by_class_name('input')
print(input.get_attribute('placeholder'))
这里我们用 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
driver = webdriver.Chrome()
driver.get('https://www.taobao.com')
wait = WebDriverWait(driver, 10)
input = wait.until(EC.presence_of_element_located((By.ID,'q')))
button = WebDriverWait(driver, 10).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 秒还不可点击,也就是没有加载出来,就抛出异常。
运行代码,在网速较佳的情况下是可以成功加载出来的。输出如下:
<selenium.webdriver.remote.webelement.WebElement (session="eb19f3f58aad1c6b50366e3135e0165c", element="f52b4bd2-57d5-4323-a282-2654a2c73dc3")> <selenium.webdriver.remote.webelement.WebElement (session="eb19f3f58aad1c6b50366e3135e0165c", element="394f1c5b-96b2-4ebc-a56c-11fdcfa4d97c")>
关于等待条件,其实还有很多,这里列出几个常用的条件:
presence_of_element_located #节点加载出来,传入定位元组,如(By.ID, 'p')
visibility_of_element_located #节点可见,传入定位元组
presence_of_all_element_located #所有节点加载出来
element_to_be_clickable #节点可点击
前进和后退
平常使用浏览器时都会有前进和后退功能,Selenium 也可以完成这个操作,使用 back() 方法后退,使用 forward() 方法前进。示例如下:
import time
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.taobao.com')
driver.get('https://www.baidu.com')
driver.get('https://www.python.org')
driver.back()
time.sleep(3)
driver.forward()
driver.close()
这里我们连续访问 3 个页面,然后调用 back() 方法回到第二个页面,接下来再调用 forward() 方法又可以前进到第三个页面。
Cookies
我们也可以通过 Selenium 对 Cookies 进行操作,例如获取、添加、删除 Cookies 等。示例如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver = webdriver.get('https://www.zhihu.com/explore')
print(driver.get_cookies())
driver.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germey'})
print(driver.get_cookies())
driver.delete_all_cookies()
print(driver.get_cookies())
首先,我们访问了知乎。加载完成后,浏览器实际上已经生成 Cookies 接着,调用 get_cookies() 方法获取所有的 Cookies 然后,我们添加一个 Cookie ,这里传入一个字典,有 name、domain 和 value 等内容。接下来,再次获取所有的 Cookies。可以发现,结果就多了这一项新加的 Cookie 最后,调用 delete_all_cookies() 方法删除所有的 Cookies。再重新获取,发现结果就为空了。输出如下:
[{'domain': 'www.zhihu.com', 'httpOnly': False, 'name': 'KLBRSID', 'path': '/', 'secure': False, 'value': 'cdfcc1d45d024a211bb7144f66bda2cf|1619697020|1619697019'}, {'domain': '.zhihu.com', 'expiry': 1651233021, 'httpOnly':
False, 'name': 'Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1619697021'},
{'domain': '.zhihu.com', 'httpOnly': False, 'name': 'Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1619697021'}, {'domain': '.zhihu.com', 'expiry': 1714305019, 'httpOnly': False, 'name':
'd_c0', 'path': '/', 'secure': False, 'value': '"AJBdSHZxBxOPTk1PCJg-4ZU2sMgEyeWygio=|1619697019"'}, {'domain':
'.zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': '8f73e20b-9163-4405-b542-44fa03cc732a'}, {'domain': '.zhihu.com', 'expiry': 1682769019, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': '828388a0-6430-4501-90b7-71811f11deba'}]
[{'domain': '.www.zhihu.com', 'httpOnly': False, 'name': 'name', 'path': '/', 'secure': True, 'value': 'germey'}, {'domain': 'www.zhihu.com', 'httpOnly': False, 'name': 'KLBRSID', 'path': '/', 'secure': False, 'value': 'cdfcc1d45d024a211bb7144f66bda2cf|1619697020|1619697019'}, {'domain': '.zhihu.com', 'expiry': 1651233021, 'httpOnly': False, 'name': 'Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1619697021'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': 'Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1619697021'}, {'domain': '.zhihu.com', 'expiry': 1714305019, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '"AJBdSHZxBxOPTk1PCJg-4ZU2sMgEyeWygio=|1619697019"'}, {'domain': 'www.zhihu.com', 'httpOnly': False, 'name': 'SESSIONID', 'path': '/', 'secure': False, 'value': '4VAxkSEep88bTts7VTjg14CFrClAsxBlH1sveC5cJLw'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': '8f73e20b-9163-4405-b542-44fa03cc732a'}, {'domain': '.zhihu.com', 'expiry': 1682769019, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': '828388a0-6430-4501-90b7-71811f11deba'}]
[]
其中,get_cookies() 方法是获取所有的cookie,get_cookie(name) 是根据 cookie 的name 获取 cookie 。delete_cookie(‘key’) 删除某个cookie, delete_all_cookies() 删除所有 cookies。
选项卡管理(多页面)
在访问网页的时候,会开启一个个选项卡。在 Selenium 中,我们也可以对选项卡进行操作。示例如下:
import time
from selenium import webdriver
driver = webdriver.get('https://www.baidu.com')
driver.execute_script('window.open('https://www.taobao.com')')
print(driver.window_handles)
driver.switch_to_window('driver.window_handles[1]')
time.sleep(2)
driver.switch_to_window('driver.window_handles[0]')
控制台的输出为:
['CDwindow-D67781D6F0868F0DC81C44B7CD4EB67B', 'CDwindow-58DD3C38736C2028D2ABC8C347370703']
在这里,我们使用 execute_script() 方法,这里传入 window.open() 这个 JavaScript 语句打开一个新窗口。然后调用 window_handles 属性获取当前开启的所有的选项卡,返回的是选项卡的代号列表。如果想切换选项卡,只需要调用 switch_to_window() 方法即可,其参数为选项卡的代号。
异常处理
在使用 Selenium 的过程中,难免会遇到一些异常,例如超时、节点未找到等错误,一旦出现此类错误,程序便不会继续运行了。这里我们可以使用 try except 语句来捕获各种异常。
首先,演示一下节点未找到的异常,示例如下:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
driver.find_element_by_id('hello')
这里首先打开百度页面,然后尝试选择一个不存在的节点,此时就会遇到异常。运行后输出如下:
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element:
可以看到,这里抛出了 NoSuchElementException 异常,这通常是节点找不到的异常。为了防止程序遇到异常而中断,我们需要捕获这些异常,示例如下:
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
try:
driver.find_element_by_id('hello')
except NoSuchElementException as e:
print('No Element')
print(e)
finally:
driver.close()
这里我们使用 try except 来捕获异常。这样一旦出现错误,就进行异常处理,程序也不会中断了。
使用 Selenium 爬取淘宝商品
该案例写在另一篇博客当中