动态加载页面信息的提取[二]
上一遍博客介绍了Ajax的分析和抓取方式,这只是JavaScript动态渲染的页面的一种情形。JavaScript动态渲染的页面不止Ajax这一种。比如中国青年网 http://news.youth.cn/gn/
,它的分页部分是由JavaScript生成的,并非原始HTML代码,这其中并不包含Ajax请求。还有淘宝的页面,它即使通过Ajax获取数据,但是其Ajax接口含有很多加密参数,我们难以直接找出其规律,也很难直接分析Ajax来抓取。
为了解决这些问题,我们可以直接使用模拟浏览器运行的方式来实现,这样就可以做到在浏览器中看到是什么样,抓取的源码就是什么样。这样我们就不用再去管网页内部的JavaScript如何渲染页面,不用管网页后台的Ajax接口到底有哪些参数。
环境配置
1.selenium安装
Selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。对于一些JavaScript动态渲染的页面来说,此种抓取方式非常有效
pip3 install selenium
#因为chrome驱动的原因,今天用的是win10,装的时候报错了,加个
python -m 就好
>>>Fatal error in launcher: Unable to create process using '"'
python -m pip install 库名
2.chromdriver版本的正确下载
成功安装好了Selenium库,但是它是一个自动化测试工具,需要浏览器来配合使用
只有安装ChromeDriver,才能驱动Chrome浏览器完成相应的操作
- 下载
查看当前你的chrome版本,这里有与之对应的chromedriver版本对照表
https://blog.csdn.net/cz9025/article/details/70160273 ;
找到对应的版本之后到这个镜像站下载就好
http://npm.taobao.org/mirrors/chromedriver/
凭直觉也应该能推算出肯定要下载2018年4或5月的版本;解压出来添加至环境变量
- 测试
>>> from selenium import webdriver
>>> automation = webdriver.Chrome()
如果这时候Chrome浏览器自动弹出
selenium用法
1.访问网页
通过 get() 方法来请求网页,参数传入链接URL即可
#声明浏览器对象
automation = webdriver.Chrome()
automation.get('https://www.jd.com')
运行程序,程序为我们打开浏览器,并访问京东网
2.查找节点
Selenium可以驱动浏览器完成各种操作,比如填充表单、模拟点击等。Selenium提供了一系列查找节点的方法,可以用这些方法来获取想要的节点,以便下一步执行一些动作或者提取信息。
- 单个节点
比如,想要从淘宝页面中提取搜索框这个节点,首先要观察它的源代码
可以发现,它的id是key。此外,还有许多其他属性,此时我们就可以用多种方式获取它了。比如,find_element_by_name()是根据name值获取,find_element_by_id()是根据id获取。另外,还有根据XPath、CSS选择器等获取的方式。
automation = webdriver.Chrome()
automation.get('https://www.jd.com')
input_one = automation.find_element_by_id('key')
input_two = automation.find_element_by_class_name('text')
print(input_one,input_two)
#通过ID,和CSS选择器获取的是同一节点
>>>
获取京东网的商品搜索框
另外,Selenium还提供了通用方法find_element(),它需要传入两个参数:查找方式By和值。实际上,它就是find_element_by_id()这种方法的通用函数版本,比如find_element_by_id(id)就等价于find_element(By.ID, id),二者得到的结果完全一致。我们用代码实现一下:
- 多个节点
如果查找的目标在网页中只有一个,那么完全可以用find_element()方法。但如果有多个节点,再用find_element()方法查找,就只能得到第一个节点了。如果要查找所有满足条件的节点,需要用find_elements()这样的方法。注意,在这个方法的名称中,element多了一个s。
例如要获取京东左侧物品分类栏的所有节点
分析一波,发现它们的样式都是cate_menu_item
automation.get('https://www.jd.com')
input_three = browser.find_elements_by_class_name('cate_menu_item')
print(input_three)
>>>
[, , , ,...]
发现输出的是一个列表
3.节点交互
Selenium可以让浏览器模拟执行一些动作。比较常见的用法有:输入文字时用send_keys()方法,清空文字时用clear()方法,点击按钮时用click()方法。示例如下:
automation = webdriver.Chrome()
input = automation.find_element_by_id('key')
input.send_keys('小米8')
input.send_keys(Keys.ENTER)
程序会打开浏览器,访问京东网,在自动在搜索框输入小米8 ,并自动搜索
- 以下程序和上面程序等价,需要获取到搜索按钮的节点,但更容易理解
automation = webdriver.Chrome()
automation.get('https://www.jd.com')
input = automation.find_element_by_id('key')
input.send_keys('小米8')
button = automation.find_element_by_class_name('button')
button.click()
其实模拟的就是鼠标点击事件
4.执行javascript
对于某些操作,Selenium API并没有提供。比如,下拉进度条,它可以直接模拟运行JavaScript,此时使用execute_script()方法即可实现,代码如下:
from selenium import webdriver
automation = webdriver.Chrome()
automation.get('https://www.zhihu.com/explore')
automation.execute_script('window.scrollTo(0, document.body.scrollHeight)')
automation.execute_script('alert("杨浩成")')
这里就利用execute_script()方法将进度条下拉到最底部,然后弹出alert提示框。
所以说有了这个方法,基本上API没有提供的所有功能都可以用执行JavaScript的方式来实现了。
5.获取节点信息
- 获取属性
过get_attribute()方法,然后传入想要获取的属性名,就可以得到它的值了
from selenium import webdriver
automation = webdriver.Chrome()
automation.get('https://www.jd.com')
div = automation.find_element_by_class_name('lazyimg_img')
print(div.get_attribute('src'))
>>>https://img12.360buyimg.com/babel/s380x300_jfs/t21358/107/1797333452/29670/280abb3c/5b35c51bN0004362d.jpg!q90!cc_190x150
跟据class获取图片,通过src属性获取图片链接
- 获取文本值
每个WebElement节点都有text属性,直接调用这个属性就可以得到节点内部的文本信息,这相当于Beautiful Soup的get_text()方法、pyquery的text()方法,示例如下:
from selenium import webdriver
automation = webdriver.Chrome()
automation.get('https://www.jd.com')
li_list = automation.find_elements_by_class_name('cate_menu_item')
for iterm in li_list:
print(iterm.text)
运行效果:
- 获取id、位置、标签名和大小
另外,WebElement节点还有一些其他属性,比如id属性可以获取节点id,location属性可以获取该节点在页面中的相对位置,tag_name属性可以获取标签名称,size属性可以获取节点的大小,也就是宽高。方法都是一样的,这里就不做赘述了。
6.延时等待
在Selenium中,get()方法会在网页框架加载结束后结束执行,此时如果获取page_source,可能并不是浏览器完全加载完成的页面,如果某些页面有额外的Ajax请求,我们在网页源代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来。
这里等待的方式有两种:一种是隐式等待,一种是显式等待。
1.隐式等待
当使用隐式等待执行测试的时候,如果Selenium没有在DOM中找到节点,将继续等待,超出设定时间后,则抛出找不到节点的异常。换句话说,当查找节点而节点并没有立即出现的时候,隐式等待将等待一段时间再查找DOM,默认的时间是0。示例如下:
from selenium import webdriver
automation = webdriver.Chrome()
automation.implicitly_wait(0.1)
automation.get('https://www.jd.com')
li_list = automation.find_elements_by_class_name('cate_menu_item')
for iterm in li_list:
print(iterm.text)
用implicitly_wait()方法实现隐式等待。
2.显示等待
显式等待方法,它指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常。这里就不在赘述了
小结
通过selenium对chromedriver的自动化控制,有以下优点:
- 动态加载的网页数据也能够爬取;
- 速度快,通过爬虫来实现抢课,抢票已经见怪不怪了,下篇博客将介绍一下实现12306订票系统的抢票程序,用到本篇的知识结合Xpath技术实现
关于作者
个人博客: http://yhch.xyz