python3爬虫(8)--动态渲染页面使用Selenium库模拟浏览器抓取数据

在使用Ajax采集数据时,有些Ajax接口含有很多的加密参数,直接很难发现规律。此时,就可以使用模拟浏览器运行的方式来采集。Python 提供了许多模拟浏览器运行的库,如Selenium、Splash、PyV8、Ghost等。

一、模拟浏览器爬取数据Selenium的使用。

1.1、安装准备工作

安装selenium库:pip install selenium

(1)、谷歌(Chrome)浏览器需要ChromeDriver 驱动的安装与配置

  • 查看浏览器版本号:点击Chrome右上角的菜单->帮助->关于Google Chrome即可查看,我这里的版本版本 79.0.3945.130
  • ChromeDriver下载:https://chromedriver.storage.googleapis.com/index.html
  • 下载对应版本及对应系统:chromedriver_win32.zip
  • 环境变量配置:这里是windows系统我们直接解压后将chromedriver.exe文件拖到Python(anaconda)的Scripts目录下。
  • cmd窗口测试:cmd-->chromedriver,输出Starting ChromeDriver ...on port ...,则安装配置成功。
  • 代码测试: 执行下列代码,会弹出一个空白的Chrome浏览器
  • from selenium import webdriver
    browser=webdriver.Chrome()

(2)、火狐(Firefor)浏览器需要GeckoDriver的安装与配置

  • 下载地址:https://github.com/mozilla/geckodriver/releases
  • 下载对应版本:如:geckodriver-v0.26.0-win64.zip
  • 环境配置:windows系统直接解压拖到Python(anaconda)的Scripts目录下。
  • cmd窗口测试:cmd-->geckodriver,输出...geckodriver...,则安装配置成功。
  • 代码测试: 执行下列代码,会弹出一个空白的Firefor浏览器
  • from selenium import webdriver
    browser=webdriver.Firefox()

1.2、声明浏览器对象

  • from selenium import webdriver  #导入库
  • browser =webdriver. Chrome()   #谷歌浏览器的声明及初始化
  • browser =webdriver. Firefox()    #火狐浏览器的声明及初始化
  • browser =webdriver. Edge()       #Edge浏览器的声明及初始化
  • browser =webdriver. PhantomJS() #无界面浏览器PhantomJS的声明及初始化
  • browser =webdriver. Safari()     #Safari浏览器的声明及初始化
  • ......

完成浏览器对象的初始化并将其赋值为browser对象。调用browser对象,让其执行各个动作以模拟浏览器操作。

1.3、访问页面,get()方法

from selenium import webdriver
browser=webdriver.Chrome()
browser.get('https://www.taobao.com/') #访问网页
url= browser.current_url
print(url) #获取url
cookies = browser.get_cookies()
print(type(cookies),cookies) #获取cookies,格式:<class 'list'>
SC = browser.page_source
print(type(SC),SC) #获取源代码,格式:<class 'str'>

1.4、查找节点

单个节点:

获取单个节点:方法 说明:返回WebElement类型 示例:
get() 请求访问页面 from selenium import webdriver
browser=webdriver.Chrome()
browser.get('https://www.taobao.com/')
find_element_by_id 根据id属性的值获取节点 input_01=browser.find_element_by_id('q') #定位到淘宝的搜索框
find_element_by_name 根据name属性值获取节点 input_02=browser.find_element_by_name('q') 
find_element_by_class_name 根据class属性值获取节点 input_03=browser.find_element_by_class_name('search-combobox-input-wrap')
find_element_by_xpath 根据Xpath选择器定位 input_04=browser.find_element_by_xpath('//*[@id="q"]')
find_element_by_css_selector 根据CSS选择器定位 input_05=browser.find_element_by_css_selector('#q')
find_element_by_link_text 根据文本定位 input_06=browser.find_element_by_link_text("淘宝网")
find_element_by_partial_link_text 根据局部文本定位 input_07=browser.find_element_by_partial_link_text("淘宝网")
find_element_by_tag_names 根据标签名称定位 input_08=browser.find_element_by_tag_name('input')
find_element()

根据查找方法By和值定位,获取第一个满足条件的节点

from selenium.webdriver.common.by import By

input_09=browser.find_element(By.ID,'q') #注意大小写

多个节点:

返回满足条件的多个节点,其实就是把返回单个节点的element改为elements即可。每个方法都一样。如:

find_elements_by_id
find_elements_by_name
find_elements_by_class_name
find_elements_by_xpath
find_elements_by_css_selector
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_names
find_elements()

1.5、节点交互

Selenium驱动浏览器执行一些操作。比较常见的用法:

案例:

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('python')
time.sleep(6)
input.clear()
input.send_keys(r'python爬虫书籍')
button=browser.find_element_by_class_name('btn-search')
button.click()
'''
代码解释:
这里首先驱动浏览器打开淘宝,然后用find_element_by_id()方法获取输入框,
然后用send_keys()方法输入iPhone文字,
等待6秒后用clear()方法清空输入框,
再次调用send keys()方法输入iPad文字,
再用find_element_by_class_name()方法获取搜索按钮,
最后调用click()方法完成点击搜索动作。
'''

1.6、动作链

一般来说我们与页面的交互用find_element和节点交互就可以完成了。但是一些更复杂的动作,类似于拖动,双击,长按等等。这时候就需要用到Action Chains(动作链)了。

常用动作方法:

方法 说明
click(on_element=None) 左键单击传入的元素,如果不传入的话,点击鼠标当前位置。
context_click(on_element=None) 右键单击。
double_click(on_element=None) 双击。
click_and_hold(on_element=None) 点击并抓起
drag_and_drop(source, target) 在source元素上点击抓起,移动到target元素上松开放下。
drag_and_drop_by_offset(source, xoffset, yoffset) 在source元素上点击抓起,移动到相对于source元素偏移xoffset和yoffset的坐标位置放下。
send_keys(*keys_to_send) 将键发送到当前聚焦的元素。
send_keys_to_element(element, *keys_to_send) 将键发送到指定的元素。
reset_actions()

清除已经存储的动作。

更多 https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains

案例:

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() #⑧
'''
代码注释:
①:申明Chrome浏览器对象;
②:请求访问页面;
③:切换Frame节点,后面有单独解释;
④:定位拖拽节点;
⑤:定位目标节点;
⑥:声明ActionChains对象并将其赋值为actions变量;
⑦:调用actions变量的drag_and_drop()方法;
⑧:调用perform()方法执行动作
'''

1.7、执行JavaScript

对于某些操作,SeleniumAPI没有提供。如:下拉进度条,利用模拟运行JavaScript的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")')
'''
这里就利用execute_script()方法将进度条下拉到最底部,然后弹出alert提示框。
'''

1.8、获取节点信息

(1)、通过page_source属性可以获取网页的源代码,可以用解析库(如Xpath、Beautiful Soup、pyquery等)来提取信息。

(2)、Selenium提供了选择节点的方法,返回的是WebElement类型,它也有相关的方法来直接提取节点信息。

  • 获取属性:可以使用get_attribute()方法,但需要事先选中节点。
from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
browser.get(url)
logo = browser.find_element_by_id('special')
print(logo)
print(logo.get_attribute('class'))
  • 获取文本:每个WebElement节点都有text属性,直接调用这个属性即可。
  • from selenium import webdriver
    
    browser = webdriver.Chrome()
    url = 'https://www.zhihu.com/explore'
    browser.get(url)
    input = browser.find_element_by_xpath('//*[@id="root"]/div/div[2]/header/div[1]/ul/li[1]/a')
    print(input.text) #首页
  • 获取节点id、位置、标签名、大小(宽高)
  • from selenium import webdriver
    
    browser = webdriver.Chrome()
    url = 'https://www.zhihu.com/explore'
    browser.get(url)
    input = browser.find_element_by_xpath('//*[@id="root"]')
    print(input.id) #节点id:017b8ce6-0706-4b23-8c66-2a46c96858a1
    print(input.location) #节点在页面中的相对位置:{'x': 0, 'y': 0}
    print(input.tag_name) #标签名称:div
    print(input.size) #节点大小:{'height': 3665, 'width': 1017}

1.9、切换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节点(这是不能找到的),
如果找不到的话,就会抛出NoSuchElementException异常,
异常被捕捉之后,就会输出NO LOGO。
接下来,重新切换回父级Frame,然后再次重新获取节点,发现此时可以成功获取了。
所以,当页面中包含子Frame时,如果想获取子Frame中的节点,
需要先调用switch_to.frame()方法切换到对应的Frame,然后再进行操作。
'''

1.10、延时等待

为了确保节点都全部加载出来,在进行解析,需要设置延时等待。方式有两种:隐式等待和显式等待。

隐式等待:

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

from selenium import webdriver
 
browser = webdriver.Chrome()
browser.implicitly_wait(20) #implicitly_wait()方法实现了隐式等待。
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)
'''
超出设定时间后,则抛出找不到节点的异常。但是隐式等待的时间是固定的,不够灵活。
'''

显式等待:

指定要查找的节点,设置不同的等待条件,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常。

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, 30)
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)

等待条件

含义

title_is

标题是某内容

title_contains

标题包含某内容

presence_of_element_located

节点加载出来,传入定位元组,如(By.ID, '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

加载并切换

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

是否出现警告

更多 http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions

1.11、前进和后退

平常使用浏览器时都有前进和后退功能,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()方法又可以前进到第三个页面。
'''

1.12、获取、删除、添加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())

1.13、选项卡管理

在访问网页的时候,会开启一个个选项卡。在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')

首先访问了百度,然后调用了execute_script()方法,这里传入window.open()这个JavaScript语句新开启一个选项卡。接下来,我们想切换到该选项卡。这里调用window_handles属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要调用switch_to_window()方法即可,其中参数是选项卡的代号。这里我们将第二个选项卡代号传入,即跳转到第二个选项卡,接下来在第二个选项卡下打开一个新页面,然后切换回第一个选项卡重新调用switch_to_window()方法,再执行其他操作即可。

1.14、异常处理

官网文档:http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions

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

本文来源:崔庆才 著 《python3网络爬虫开发实战》学习笔记

发布了110 篇原创文章 · 获赞 109 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_41685388/article/details/104080942