动态渲染页面爬取

JavaScript动态渲染页面,他的分页部分有js生成,并非原始的HTML代码

如淘宝使用request只能请求到页面大致框架,无法拿到商品数据,或部分网站换页时url不发生变化。

或者使用Ajax开发的网站(如今日头条),他的接口有很多加密处理(搜索信息后信息URL经过加密处理,很难找到规律)

Python中提供了很多模拟浏览器的库,selenium ,pyV8等,来解决动态渲染的页面。

selenium的使用

selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作。同时也可以获取浏览器当前呈现的页面渲染,做到可见即可爬。用于动态页面爬取(经过js渲染的页面)

准备工作

以谷歌浏览器为例,了解selenium的使用

  1. 需要安装好谷歌浏览器,并配置好ChromeDriver

  2. 先去谷歌浏览器看版本,下载Chromedriver:http://chromedriver.storage.googleapis.com/index.html

  3. 配置环境变量:将下载好的可执行文件放入python的Scripts目录下,添加至环境变量

  4. 验证:在cmd中执行ChromeDriver,会返回版本号

    1561100027681

安装selecnium库

解决安装报错问题:https://blog.csdn.net/ever_peng/article/details/80089095

from selenium import webdriver
brower = webdriver.Chrome()
#打开一个新的Crome页面则环境配置成功 
基本使用
from selenium import webdriver#用于打开浏览器
from selenium.webdriver.support.wait import WebDriverWait#用于设置等待时间
from selenium.webdriver.common.keys import Keys#用于按键操作
from selenium.webdriver.common.by import By#用于匹配节点
from selenium.webdriver.support import expected_conditions as EC#用于等待时判断节点是否加载,如果没有,继续在规定的时间加载
#打开谷歌浏览器
brower = webdriver.Chrome()
try:
    #请求百度网站
    brower.get('https://www.baidu.com')
    #获取ID属性为kw的节点,用于输入要搜索的信息
    input = brower.find_element_by_id('kw')
    #在搜索框输入python
    input.send_keys('python')
    input.send_keys(Keys.ENTER)#keys是获取键盘的按键相当于一个回车操作
    wait = WebDriverWait(brower,10)#等待页面加载
    #判断页面是否响应到id属性为content_left的内容
    #写的时候是以元组形式 注意括号的个数
    wait.until(EC.presence_of_element_located((By.ID, 'content_left')))

finally:
    #关闭浏览器
	#brower.close()
    pass

声明浏览器对象

selenium支持多种浏览器,如chrome Firefox 也支持无界面的浏览器,如PhantomJS

from selenium import webdriver
brower = webdriver.Chrome()#谷歌
brower = webdriver.Firefox ()#火狐
brower = webdriver.PhantomJS()#phantomJS
#完成浏览器对象的初始化并将其赋值为brower这个对象。由此调用broweer对象,让浏览器执行对应的操作。

访问页面

使用get()方法来请求网页,参数传输链接url即可,如get方法访问淘宝页面,打印源代码

from selenium import webdriver

brower = webdriver.Chrome()
brower.get('https://www.taobao.com')
#获取响应到的网页源码
print(brower.page_source)

查找节点

selenium可以驱动浏览器完成各种操作,如填充表格、模拟点击等操作,如向某个输入框输入文本操作

单个节点

根据xpath css选择器进行选择

#获取id属性为“q”的节点
input_1 = brower.find_element_by_id('q')
input_2 = brower.find_element_by_css_selector('#q')
input_3 = brower.find_element_by_xpath('//*[@id='q']')
print(input_1)
print(type(input_1))
#<selenium.webdriver.remote.webelement.WebElement (session="af859fdfb659b14f954adaec944bb0ef", element="0.7941017431137989-1")>
#<class 'selenium.webdriver.remote.webelement.WebElement'>
返回结果为webElement类型

所有获取单个节点的方法

browser.find_element_by_id
browser.find_element_by_name
browser.find_element_by_class_name
#使用class_name匹配节点时,如果该节点class属性有多个值时,只需要用其中一个值进行匹配,多个值同时匹配则产生异常
browser.find_element_by_tag_name
browser.find_element_by_link_text
browser.find_element_by_css_selector
browser.find_element_by_xpath

此外,selenium还提供了通用方法find_element()

他需要传入两个参数,查找方式By和值,实际为browser.find_element_by_id()的通用方法

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

from selenium.webdriver.common.by import By
browser.find_element(By.ID, 'q')
多个节点

要匹配多个节点时使用find_element()只能获取到一个节点,所以此时需要find_elements()方法,两个之间的区别为匹配多个节点时要用elements

节点交互

selenium可以驱动浏览器做一系列操作,常见的send_keys()方法,清空文字是使用clear()方法,点击按钮click()

练习:使用selenium请求京东,在搜索框输入荣耀20,然后等页面加载出内容后,睡眠3秒,再去搜索华为p30
节点.clear() 	节点.click() 		节点.send_keys() 
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
import time
brower = webdriver.Chrome()
try:
    brower.get('https://www.jd.com/')
    input = brower.find_element_by_id('key')
    input.send_keys('荣耀20')
    input.send_keys(Keys.ENTER)
    #按钮操作
    #button = brower.find_element+by_class_name('button')
    wait = WebDriverWait(brower,3)#等待页面加载
    time.sleep(3)
    print('3秒后')
    input = brower.find_element_by_id('key')
    input.clear()
    input.send_keys('荣耀P30')
    input.send_keys(Keys.ENTER) 
finally:
    pass
如果class属性里面有两个值,中间用空格隔开,用class_name匹配节点的时候只需要匹配其中一个,多了是找不到的

获取节点信息

通过page_source属性可以获取网页源代码,接着可以使用解析库(如正则,bs4等)来提取信息。

selenium也提供了节点选择方法,返回webElement类型,可以通过一些方法提取节点属性、文本等。

获取属性

可以使用get_attribute()方法来获取节点的属性,前提去先选中节点。

#获取猫眼网的图片链接
from selenium import webdriver
brower = webdriver.Chrome()

brower.get('https://maoyan.com/board/4')
input_2 = brower.find_elements_by_xpath('//img[@class="board-img"]')#注意这里是elements
print(type(input_2))
for i in input_2:
    print(i.get_attribute('data-src'))
获取文本

每一个web element对象都有一个text属性,通过该属性可以获取节点的文本信息

#获取top100电影名称
name = browser.find_elements_by_xpath( '//p[@class="name"]/a') print(len(name)) 
for i in name:    
    print(i.text)

延时等待

selenium中get()方法会在网页框架加载结束后执行,此时如果获取page_source获取网页源码可能得到结果不是浏览器完全加载后的页面内容。

如果一些页面有额外的ajax请求,则在此处需要延时等待一段时间,确保信息已经加载出来。

延时等待分为:显式等待和隐式等待

隐式等待

使用隐式等待的时候,如果selenium没有在DOM中找到节点将继续等待,直到超时设定时间后抛出timeout异常,默认时间为0,以秒为单位。

from selenium import webdriver
brower = webdriver.Chrome()
brower.implicitly_wait(10)
brower.get('url')

获取淘宝首页内容,查看设置延时等待与不设置响应的内容是否是一致的

如果有延时等待,则程序在get()请求后开始在延时时给定的时间内加载数据,同时匹配网页内容。

from selenium import webdriver 
browser = webdriver.Chrome() 
browser.implicitly_wait(20) 
browser.get('https://www.taobao .com/') 
#获取信息 
# print(browser.page_source) 
print(len(browser.find_elements _by_xpath('//h4[@class="aall"]'))) browser.close()
显式等待

隐式等待效果一般,因为只规定了固定的时间,而页面加载信息时间会受到网络条件影响

显式等待它指定要查找的内容,然后指定时间进行等待。如果在规定时间加载到这个节 点则返回查找的节点,如果在规定时间依然没有加载到该节点数据,则抛出异常

移动右侧下拉条

#将下拉条从顶部拖拽到中间位置 
brower.execute_script('window.scrollTo(0,document.body.scrollHeight/2)')

前进和后退

使用back()昂发进行后退,使用forward()方法进行前进

from selenium import webdriver
import time
browser=webdriver.Chrome()
browser.get('https://www.baidu.com/')
browser.get('https://www.taobao.com/')
browser.get('https://www.jd.com/')

browser.back()
time.sleep(10)
browser.forward()

捕获异常

from selenium.common.exceptions import TimeoutException
#导入异常
try : 
    pass
except TimeoutException:
    pass
finally:
    pass