利用Selenium 可以驱动浏览器执行特定的动作,如点击、下拉等操作, 同时还可以获取浏览器当前呈现的页面的源代码 ,做到可见即可爬。
- 基本使用
- 示例:
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('https://www.baidu.com') input = browser.find_element_by_id('kw') input.send_keys('Python') input.send_keys(Keys.ENTER) wait = WebDriverWait(browser,10) wait.until(EC.presence_of_element_located((By.ID,'content_left'))) print(browser.current_url) print(browser.get_cookies()) print(browser.page_source) finally: browser.close()
运行代码后,会自动弹出一个 Chrome 浏览器。首先会跳转到百度,然后在搜索框中输入 Python,接着跳转到搜索页。 搜索结果加载出来后,控制台分别会输出当前的 URL 、当前的 Cookies 和网页源代码.
-
如果用 Selenium 来驱动浏览器加载网页的话,就可以直接拿到 JavaScript 渲染的结果,不用担心使用的是什么加密系统。声明浏览器对象
- 声明浏览器对象
- Selenium 支持非常多浏览器,如 Chrome/ Firefox/ Edge等,还有 Android 等手机端的浏览器,也支持无界面浏览器 PhantomJS。
- 初始化方式:
from selenium import webdriver browser = webdriver.Chrome() browser = webdriver.Firefox() browser = webdriver.Edge() browser = webdriver.PhantomJS() browser = webdriver.Safari()
浏览器对象的初始化并将其赋值为 browser对象。接下来,就是调用 browser 对象,让其执行各个动作以模拟浏览器操作
- 初始化方式:
- Selenium 支持非常多浏览器,如 Chrome/ Firefox/ Edge等,还有 Android 等手机端的浏览器,也支持无界面浏览器 PhantomJS。
- 访问页面
- 用 get()方法来请求网页,参数传入链接 URL 即可。如用 get()方法访问淘宝, 然后输出源代码,代码:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com/') print(browser.page_source)
弹出了 Chrome 浏览器并且自动访问了淘宝,然后控制台输出了淘宝页面的源代码, 随后浏览器关闭。可以实现浏览器的驱动并获取网页源码
- 用 get()方法来请求网页,参数传入链接 URL 即可。如用 get()方法访问淘宝, 然后输出源代码,代码:
- 查找节点
- Selenium 可以驱动浏览器完成各种操作,如填充表单、模拟点击等。Selenium 提供了系列查找节点的方法,我们可以用这些方法来获取想要的节点,以便执行一些动作或者提取信息。
- 单个节点
- 如,要从淘宝页面中提取搜索框这个节点,观察源代码(它的 id 是 q, name 也是q。此时就可以用多种方式获取它。如,find_element_by_name()是根据 name 值获取,find element_by_id()是根据 id 获取。 还有根据 XPath、css 选择器等获取的方式):
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com') input_first = browser.find_element_by_id('q') input_second = browser.find_element_by_xpath('//*[@id ="q"]') input_third = browser.find_element_by_css_selector('#q') print(input_first, input_second, input_third) browser.close()
这里使用 3 种方式获取输入框,分别根据 ID, CSS 选择器和 XPath 获取,它们返回的结果完全一致。
- 列出所有获取单个节点的方法:
- 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(By.ID, id),得到的结果完全一致。代码:
from selenium import webdriver from selenium.webdriver.common.by import By browser = webdriver.Chrome() browser.get('https://www.taobao.com') input_first = browser.find_element(By.ID, 'q') print(input_first) browser.close()
查找方式的功能与上面列举函数一致,参数更加灵活。
- 如,要从淘宝页面中提取搜索框这个节点,观察源代码(它的 id 是 q, name 也是q。此时就可以用多种方式获取它。如,find_element_by_name()是根据 name 值获取,find element_by_id()是根据 id 获取。 还有根据 XPath、css 选择器等获取的方式):
- 多个节点
- 如果有多个节点,用 find_element()方法查找,就只能得到第一个节点。如果要查找所有满足条件的节点, 需要用 find_elements()方法。注意,这个方法名称中,element 多了一个s
- 查找淘宝侧边导航条的所有条目:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com') lis = browser.find_elements_by_css_selector('.service-bd li') print(lis) browser.close()
得到的内容变成了列表类型,列表中的每个节点都是 WebElement 类型。
如果用 find_element()方法,只能获取匹配的第一个节点,结果是 WebElement类型。如果用 find_elements()方法,结果是列表类型,列表中的每个节点是 WebElement 类型。- 列出所有获取多个节点的方法:
- find_elements_by_id
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_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')
- 查找淘宝侧边导航条的所有条目:
- 如果有多个节点,用 find_element()方法查找,就只能得到第一个节点。如果要查找所有满足条件的节点, 需要用 find_elements()方法。注意,这个方法名称中,element 多了一个s
- 节点互交
- Selenium 可以让浏览器模拟执行一些动作。常见的用法有:输入文字时用 send_keys()方法,清空文字时用 clear()方法,点击按钮时用 click()方法。示例:
from selenium import webdriver import time browser = webdriver.Chrome() browser.get('https://www.taobao.com') 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 ()方法完成搜索动作。
-
更多的操作参见官方文档交互动作介绍:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。
- Selenium 可以让浏览器模拟执行一些动作。常见的用法有:输入文字时用 send_keys()方法,清空文字时用 clear()方法,点击按钮时用 click()方法。示例:
- 动作链
- 动作链 没有特定的执行对象,如:鼠标拖曳、键盘按键等。
- 如:现在实现一个节点的拖曳操作,将某个节点从一处拖曳到另外一处,实现代码:
from selenium import webdriver from selenium.webdriver import ActionChains browser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?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()方法执行动作。
- 更多动作链操作 官方文档:http://selenium-python.readthedocs.io/api.html#moduleselenium.webdriver.common.action_chains
- 执行JavaScript
- 某些操作,Selenium API 并没有提供。如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script()方法即可实现,(注意:execute_script):
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore') browser.execute_script('window.scrollTo(0,document.body.scrollHeight)') browser.execute_script('alert("To Bottom")')
- 某些操作,Selenium API 并没有提供。如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script()方法即可实现,(注意:execute_script):
- 获取节点信息
- 通过 page_source 属性可以获取网页的源代码,接着使用解析库(如正则表式、Beautiful Soup、pyquery 等)提取信息。
- 获取属性
- 可以使用 get_attribute ()方法来获取节点的属性,但是其前提是先选中这个节点,示例:
from selenium import webdriver browser = webdriver.Chrome() url = 'https://zhihu.com/explore' browser.get(url) logo = browser.find_element_by_id('zh-top-link-logo') print(logo) print(logo.get_attribute('class'))
结果获取知乎的 logo 节点,最后打印出它的 class。
通过 get_attribute()方法,然后传人想要获取的属性名,就可以得到它的值了
- 可以使用 get_attribute ()方法来获取节点的属性,但是其前提是先选中这个节点,示例:
- 获取文本值
- 每个 WebElement 节点都有 text 属性,直接调用这个属性就可以得到节点内部的文本信息,相当于 Beautiful Soup 的 get_text()方法、 pyquery 的 text()方法。示例:
from selenium import webdriver browser = webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_class_name('zu-top-add-question') print(input.text)
- 每个 WebElement 节点都有 text 属性,直接调用这个属性就可以得到节点内部的文本信息,相当于 Beautiful Soup 的 get_text()方法、 pyquery 的 text()方法。示例:
- 获取id、位置、标签名和大小
- Web Element 节点还有 些其他属性,如 id 属性可以获取节点 id,location 属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size 属性可以获取节点的大小(宽高),示例:
from selenium import webdriver browser =webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_class_name('zu-top-add-question') print(input.text) print(input.id) print(input.location) print(input.size) print(input.tag_name)
首先获得“提问”按钮节点,再调用其 id、location、tag_name、size 属性来获取对应的属性值
- Web Element 节点还有 些其他属性,如 id 属性可以获取节点 id,location 属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size 属性可以获取节点的大小(宽高),示例:
-
切换Frame
-
网页中有种节点叫作 iframe,也就是子 Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium 打开页面后,默认在父级 Frame 里面操作,而此时如果页面中还有子Frame,是不能获取到子 Frame 里面的节点的。这时就需要使用 switch_to.frame()方法来切换Frame。示例:
import time from selenium import webdriver from selenium.common.exceptions import NoSuchElementException browser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?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_to.frame()方法切换到子 Frame 里,再尝试获取父级 Frame 里的 logo 节点(这是不能找到的),如果找不到的话,就会抛出 NoSuchEle entException 异常,异常被捕捉之后,就会输出 NO LOGO。接下来,重新切换父级 Frame, 然后再次重新获取节点,发现此时可以成功获取了。
当页面中包含子 Frame 时,如果想获取子 Frame 中的节点,需要先调用 switch_to.frame() 方法切换到对应的 Frame,再进行操作。
-
- 延时等待
- Selenium 中, get()方法会在网页框架加载结束后结束执行,此时如果获取 page_source ,可能并不是浏览器完全加载完成的页面,某些页面有额外的 Ajax 请求,在网页惊代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来。
- 延时等待方式有两种:隐式等待和显式等待
- 隐式等待
- 使用隐式等待执行测试的时候,如果 Selenium 没有在 DOM 中找到节点,将继续等待,超出设定时间后(默认时间为 0 ),则抛出找不到节点的异常。示例:
from selenium import webdriver browser = webdriver.Chrome() browser.implicitly_wait(10) browser.get('https://www.zhihu.com/explore') input = browser.find_element_by_class_name('zu-top-add-question') print(input)
用 implicitly_wait()方法实现隐式等待。
- 使用隐式等待执行测试的时候,如果 Selenium 没有在 DOM 中找到节点,将继续等待,超出设定时间后(默认时间为 0 ),则抛出找不到节点的异常。示例:
- 显式等待
- 指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点, 则抛出超时异常。示例:
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('https://www.taobao.com/') 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:节点加载出来,传入定位元组,如(By.ID,‘q’)
- visibility_of_element_located:节点课件,传入定位元组
- visibility_of:可见,传入节点对象
- presence_of_all_elements_ilocated:所有节点加载出来
- text_to_be_present_in_element:某个节点文本包含某文字
- text_to_be_present_in_element_value:某个节点值包含某文字
- frame_to_be_available_and_switch_to_it:加载并切换
- invisibility_of_element_located:节点不可见
- element_to_be_clickable:节点可点击
- staleness_of:判断一个节点是否仍在DOM,可判断页面是否已经刷新
- element_to_be_selected:节点可选择,传节点对象
- element_located_to_be_selected:节点可选择,传入定位元组
- element_election_state_to_be:传入节点对象以及状态,相等返回True,否则返回False
- element_located_selection_state_to_be:传入定位元组。以及状态,相等返回True,否则返回False
- alert_is_present:是否出现警告
- 更多等待条件的参数及用法,参考官方文档: http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions。
- 对于按钮,更改一下等待条件,如改为 element_to_be_clickable,也就是可点击,所以找按钮时查找 CSS 选择器为 .btn_search 的按钮,如果 10 秒内它是可点击的,也就是成功加载,返回这个按钮节点;如果超过 10 秒还不可点击,就抛出异常.
- 指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点, 则抛出超时异常。示例:
- Selenium 中, get()方法会在网页框架加载结束后结束执行,此时如果获取 page_source ,可能并不是浏览器完全加载完成的页面,某些页面有额外的 Ajax 请求,在网页惊代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来。
- 前进和后退
- Selenium 可以完成前进和后退操作,它使用 back()方法后退,使用 forward()方法前进。示例:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com/') browser.get('https://www.taobao.com/') browser.get('https://www.python.org/') browser.back() time.sleep(1) browser.forward() browser.close()
连续访问3个页面,调用 back()方法回到第二个页面,再调用 forward()方法前进到第 3个页面。
- Selenium 可以完成前进和后退操作,它使用 back()方法后退,使用 forward()方法前进。示例:
- Cookies
- Selenium 可以方便地对 Cookies 进行 获取、添加、删除等。示例:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore') print(browser.get_cookies()) browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', '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。再重新获取,结果就为空了。
- Selenium 可以方便地对 Cookies 进行 获取、添加、删除等。示例:
- 选项卡管理
- 访问网页时候,会开启一个个选项卡。Selenium 中,也可以对选项卡进行操作。示例:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com') browser.execute_script('window.open()') print(browser.window_handles) browser.switch_to_window(browser.window_handles[1]) browser.get('https://www.taobap.com') time.sleep(1) browser.switch_to_window(browser.window_handles[0]) browser.get('https://python.org')
首先访问百度,然后调用 execute_script()方法,传入 window.open()这个 JavaScript 语句新开启一个选项卡。接下来,想切换到该选项卡,调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要调用 switch_to_window()方法即可,其中参数是选项卡的代号。将第二个选项卡代号传人,即跳转到第二个选项卡,接下来在第二个选项卡下打开一个新页面,然后切换回第一个选项卡重新调用 switch_to_window()方法,再执行其他操作。
- 访问网页时候,会开启一个个选项卡。Selenium 中,也可以对选项卡进行操作。示例:
- 异常处理
- 在使用 Selenium 过程中,可以使用 try except 语句来捕获各种异常。(模拟节点未找到异常)示例:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com') browser.find_element_by_id('hello')
首先打开百度页面,尝试选择一个并不存在的节点 ,此时就会遇到异常。捕获异常示例:
from selenium import webdriver from selenium.common.exceptions import TimeoutException, NoSuchElementException browser = webdriver.Chrome() try: browser.get('https://www.baidu.com') except TimeoutException: print('Timeout') try: browser.find_element_by_id('hello') except NoSuchElementException: print('No Element') finally: browser.close()
这里使用 try except 来捕获各类异常。如,对 find_element_by_id()查找节点方法捕获 NoSuchElementException 异常,一旦出现这样的错误,就进行异常处理,程序也不会中断。
- 更多异常类,参考官方文档 http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions。
- 在使用 Selenium 过程中,可以使用 try except 语句来捕获各种异常。(模拟节点未找到异常)示例: