使用selenium爬取动态页面

1 Selenium的使用

   selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,例如 点击 下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见既可爬。对于一些js动态渲染的页面来说,此种爬取方式非常有效。

1.1 安装selenium

1.1.1 安装:python的Selenium库

pip install selenium

验证:python命令行 import selenium 无报错信息表示安装成功

1.1.2 安装Google浏览器并配置ChromeDriver

点击Google菜单 帮助 -> 关于Google Chrome -> 查看版本号 (版本 71.0.3578.98(正式版本) (64 位))

  • 下载ChromeDriver https://chromedriver.storage.googleapis.com/index.html
    对比自己浏览器版本下载相应版本
  • 解压后把文件放到 python的script目录下 例如 'C:\Users\admin\AppData\Local\Programs\Python\Python36\Scripts'
  • 验证: cmd命令行 执行 chromedriver 输出如下信息:

表示安装 ChromeDriver配置好了
测试 在python程序中执行

from selenium import webdriver
browser = webdriver.Chrome()

程序运行后 弹出空白的Chrome浏览器 说明我们之前配置都没有问题;没有弹出重新配置 闪退可能是版本不兼容

1.2 基本使用

示例如下:

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

可以看到自动弹出浏览器 首先跳转百度 然后搜索框输入Python 接着跳转搜索结果页
搜索结果出来后 控制台输出当前URL Cookies 网页源代码
使用selenium 驱动浏览器加载网页就可以拿到js渲染结果了 不用担心什么加密系统

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

1.2.2 访问页面

   get() 方法请求页面 参数 传入链接URL 例如访问淘宝

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source)
browser.close()

弹出Chrome浏览器 自动访问淘宝 控制台输出网页源代码随后浏览器关闭

1.2.3 查找节点

   Selenium 可以驱动浏览器完成各种操作,例如填充表单,模拟点击等 例如想要完成某个输入框输入文字操作 需要查找输入框位置

1.2.3.1 单个节点:

例如淘宝页面提取搜索框节点
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_css_selector('#q')
input_third = browser.find_element_by_xpath('//*[@id="q"]')
print(input_first)
print(input_second)
print(input_third)
browser.close()

<selenium.webdriver.remote.webelement.WebElement (session="6ad823b3e396d4a9e98b186b1b8c9748", element="e85eb755-c715-4525-b1c4-9dde62c5cf2d")>
<selenium.webdriver.remote.webelement.WebElement (session="6ad823b3e396d4a9e98b186b1b8c9748", element="e85eb755-c715-4525-b1c4-9dde62c5cf2d")>
<selenium.webdriver.remote.webelement.WebElement (session="6ad823b3e396d4a9e98b186b1b8c9748", element="e85eb755-c715-4525-b1c4-9dde62c5cf2d")>

返回WebElement类型

获取单个节点的方法:

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

另外 还提供了通用方法 find_element() 需要传入两个参数:1.查找方式 2.值 例如: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()

<selenium.webdriver.remote.webelement.WebElement (session="47dd85f5b13035a2e09ce98abcc6a9a8", element="7598fb40-c5f0-437d-817e-4a921282f77a")>

1.2.3.2 多个节点:

find_elements() 查找所有满足条件的节点
示例: 查找淘宝左侧导航条的所有条目

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
lis = browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()

返回列表 每个元素是WebElement类型
获取多个节点的方法:

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

1.2.4 节点交互

  • send_keys() 输入文字
  • clear() 清空文字
  • click() 点击按钮
from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get('http://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()

首先打开淘宝 获取输入框 输入iphone文字 等待一秒 清空输入框 输入ipad 说去搜索按钮 点击按钮

官方文档交互动作 :http:selenium-python.readthedocs.io/api.html#module-selenium.webdrive.remote.webelement

1.2.5 动作链

例如鼠标拖拽 键盘按键等动作
示例:

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#module-selenium.webdriver.common.action_chains

1.2.6 执行JavaScript

对于某些操作selenium API 并没有提供 例如下拉进度条 可以使用 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")')

1.2.7 获取节点信息

获取属性 get_attribute() 方法 获取节点属性 前提是选中这个节点
获取文本值 text属性

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
lis = browser.find_elements_by_css_selector('.service-bd li a')
print(lis)
for ele in lis:
    print(ele.text)
    print(ele.get_attribute('href'))
browser.close()

1.2.8 切换frame

页面中有一种节点iframe 也就是frame 相当于当前页面的子页面 结构和外部网页结构完全一致 selenium 打开页面默认是在父级frame里面操作 如果里面还有子页面是获取不到里面节点的 需要使用 switch_to.frame() 方法切换子frame
示例:获取动作链接操作网页的logo

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')#切换到子frame
try:
  logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
  print('NO LOGO')
browser.switch_to.parent_frame()#切换到父frame
logo = browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)

1.2.9 延时等待

selenium中get方法会在网页框架加载结束后结束执行 如果此时获取page_source 可能并不是网页加载完全的页面 如果某些页面存在ajax加载 网页源代码也不一定能获取的到 所以需要延时等待一定时间,确保节点已经加载出来
两种方式

  1. 隐式等待:当使用了隐式等待执行测试的时候,如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常
  2. 显式等待:指定一个等待条件,和一个最长等待时间,程序会判断在等待时间内条件是否满足,如果满足则返回,如果不满足会继续等待,超过时间就会抛出异常

1.2.9.1 隐式等待

当使用隐式等待时 selenium没有在DOM中找到节点 将继续等待 超出设置时间后 抛出异常 默认时间是0 示例: implicitly_wait() 方法

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

browser = webdriver.Chrome()
browser.implicitly_wait(5)
browser.get('https://www.taobao.com')
input_first = browser.find_element(By.ID,'q')
print(input_first)
browser.close()

1.2.9.2 显式等待

until() 方法:如果页面加载受网络条件影响 隐式效果就不是很好

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait#引入 WebDriverWait对象

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser,10)
input = wait.until(EC.presence_of_element_located((By.ID,'q')))#调用until方法 节点出现
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))#调用until方法 按钮可点击
print(input,button)

1.2.9.3 更多等待条件

  • title_is() 标题是某内容
  • title_contains() 标题包含某内容
  • presence_of_element_located() 节点加载出来 传入定位元组 例如(By.ID,'P')
  • visibility_of_element_located() 节点可见 传入定位元组
  • visibility_of() 可见 传入节点对象
  • presence_of_all_element_located() 所有节点加载出来
  • 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_located_to_be_selected() 节点可选择 传入节点对象
  • element_selection_state_to_be() 传入节点对象以及状态 相等返回True 否则False
  • element_located_selected_to_be() 传入定位元组以及状态 相等返回True 否则False
  • alert_is_present() 是否出现警告

更多参考官方文档 http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions

1.2.10 前进和后退

forward() 方法前进 back()方法后退
示例:

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

1.2.11 Cookies

  • 获取 get_cookies()
  • 添加 add_cookie()
  • 删除 delete_all_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':'wwww.zhihu.com','value':'germery'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())

1.2.12 选项卡管理

在访问网页时会出现一个个选项卡 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.taobao.com')
time.sleep(1)
browser.switch_to_window(browser.window_handles[0])
browser.get('https://python.org')

1.2.13 异常处理

在使用selenium的过程中 难免会出现一些异常 例如 超时 节点未找到 一旦出现此类错误程序就不会执行了 使用 try except 捕获异常
示例:节点未找到

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https:www.baidu.com')
browser.find_element_by_id('hello')

NoSuchElementException Traceback (most recent call last)

防止程序遇到异常中断 捕获异常示例:

from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException

browser = webdriver.Chrome()
try:
  browser.get('https://www.baidu.com')
except TimeoutException:
  print('Time Out')
try:
  browser.find_element_by_id('hello')
except NoSuchElementException:
  print('No Element')
finally:
browser.close()

No Element

2 综合实战

爬取网易云音乐评论

from selenium import webdriver
import time
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

driver = webdriver.Chrome()
driver.maximize_window()
driver.get("http://music.163.com/#/song?id=31877470")
driver.switch_to.frame("contentFrame")
time.sleep(5)
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
driver.save_screenshot('d:/gg.png')  # 截图
for i in range(80):
    b = driver.find_element_by_xpath("//a[starts-with(@class,'zbtn znxt')]")
    b.click()
    try:
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH,"//a[@data-type='reply']")))
        print(driver.page_source)
    except NoSuchElementException:
        print('OMG')

    time.sleep(1)
driver.quit()
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容