1、简介php
接着几个月以前的(数据科学学习手札31)基于Python的网络数据采集(初级篇),在那篇文章中,咱们介绍了关于网络爬虫的基础知识(基本的请求库,基本的解析库,CSS,正则表达式等),在那篇文章中咱们只介绍了如何利用urllib、requests这样的请求库来将咱们的程序模拟成一个请求网络服务的一端,来直接取得设置好的url地址中朴素的网页内容,再利用BeautifulSoup或pyspider这样的解析库来对获取的网页内容进行解析,在初级篇中咱们也只了解到如何爬取静态网页,那是网络爬虫中最简单的部分,事实上,如今但凡是有价值的网站都或多或少存在着本身的一套反爬机制,例如利用JS脚原本控制网页中部份内容的请求和显示,使得最原始的直接修改静态目标页面url地址来更改页面的方式失效,这一部分,我在(数据科学学习手札47)基于Python的网络数据采集实战(2)中爬取马蜂窝景点页面下蜂蜂点评区域用户评论内容的时候,也详细介绍过,但以前我在全部爬虫相关的文章中介绍的内容,都离不开这样的一个过程:node
整理url规则(静态页面直接访问,JS控制的动态页面经过浏览器的开发者工具来找到真实网址和参数)python
|web
假装浏览器正则表达式
|chrome
利用urllib.urlopen()或requests.get()对目标url发起访问数据库
|npm
得到返回的网页原始内容浏览器
|网络
利用BeautifulSoup或PySpider对网页原始内容进行解析
|
结合观察到的CSS标签属性等信息,利用BeautifulSoup对象的findAll()方法提取须要的内容,利用正则表达式来完成精确提取
|
存入数据库
而本文将要介绍的一种新的网络数据采集工具就再也不是假装成浏览器端,而是基于自动化测试工具selenium来结合对应浏览器的驱动程序,开辟真实的、显性的浏览器窗口,来完成一系列动做,以应对更加动态灵活的网页;
2、selenium
2.1 介绍
selenium也是一个用于Web应用程序测试的工具。selenium测试直接运行在浏览器中,就像真正的用户在操做同样。支持的浏览器包括IE、Mozilla Firefox、Mozilla Suite、Chrome等。这个工具的主要功能是测试与浏览器的兼容性,但因为其可以真实模拟浏览器,模拟网页点击、下拉、拖拽元素等行为的功能,使得其在网络数据采集上开辟出一片天地;
2.2 环境搭建
要想基于Python(这里咱们说的是Python3,Python2,就让它在历史的长河里隐退吧。。。)来使用selenium建立爬虫程序,咱们须要:
1.安装selenium包,直接pip安装便可
2.下载浏览器(废话-_-!),以及对应的驱动程序,本文选择使用的浏览器为Chrome,须要下载chromedriver.exe,这里提供一个收录全部版本chromedriver.exe资源的地址:
http://npm.taobao.org/mirrors/chromedriver/
须要注意的是,要下载与你的浏览器版本兼容的资源,这里给一个建议:将你的Chrome浏览器更新到最新版本,再到上述地址中下载发布时间最新的chromedriver.exe;在下载完毕后,将chromedriver.exe放到你的Python根目录下,和python.exe放在一块儿,譬如我就将其放在个人anaconda环境下的对应位置:
3.测试一下~
在完成上述操做以后,咱们要检验一下咱们的环境有没有正确搭建完成,在你的Python编辑器中,写下以下语句:
from selenium import webdriver '''建立一个新的Chrome浏览器窗体''' browser = webdriver.Chrome() '''在browser对应的浏览器中访问百度首页''' browser.get('http://www.baidu.com')
若是在执行上述语句以后,顺利地打开了Chrome浏览器并访问到咱们设置的网页,则selenium+Chrome的开发环境配置完成;
2.3 利用selenium进行网络数据采集的基本流程
在本文的一开始咱们总结了以前进行网络数据采集的基本流程,下面咱们以相似的形式介绍一下selenium进行网络数据采集的基本流程:
建立浏览器(可能涉及对浏览器一些设置的预配置,如不须要采集图片时设置禁止加载图片以提高访问速度)
|
利用.get()方法直接打开指定url地址
|
利用.page_source()方法获取当前主窗口(浏览器中可能同时打开多个网页窗口,这时须要利用页面句柄来指定咱们关注的主窗口网页)页面对应的网页内容
|
利用BeautifulSoup或pyspider等解析库对指定的网页内容进行解析
|
结合观察到的CSS标签属性等信息,利用BeautifulSoup对象的findAll()方法提取须要的内容,利用正则表达式来完成精确提取
|
存入数据库
能够看出,利用selenium来进行网络数据采集与以前的方法最大的不一样点在于对目标网页发起请求的过程,在使用selenium时,咱们无需再假装浏览器,且有了很是丰富的浏览器动做能够设置,譬如说以前咱们须要对页面进行翻页操做,主要是经过修改url中对应控制页面值的参数来完成,因此在遇到JS控制的动态网页时,能够不须要去费心寻找控制对应资源翻页的真实url地址,只须要在selenium中,经过其内置的丰富的定位方法对页面中的翻页按钮进行定位 ,再经过对定位到的元素运用.click(),便可实现真实的翻页操做,下面咱们根据上述过程当中列出的selenium部分,涉及到的经常使用方法进行介绍以及举例说明:
3、selenium经常使用操做
3.1 浏览器配置部分
在调出一个真实的浏览器对象以前,咱们能够结合实际须要对浏览器的设置进行参数配置,这在selenium中是经过对应浏览器的XXXOptions类来设置的,例如本文只介绍Chrome浏览器,则咱们经过ChromeOptions类中的方法来实现浏览器预配置,下面咱们来了解一下ChromeOptions类:
ChromeOptions:
ChromeOptions是一个在selenium建立Chrome浏览器以前,对该浏览器对象进行预配置的类,其主要功能有添加Chrome启动参数、修改Chrome设置、添加扩展应用等,如:
1.禁止网页中图片加载
from selenium import webdriver '''建立一个新的Chrome浏览器窗体,经过add_experimental_option()方法来设置禁止图片加载''' chrome_options = webdriver.ChromeOptions() prefs = {"profile.managed_default_content_settings.images": 2} chrome_options.add_experimental_option("prefs", prefs) browser = webdriver.Chrome(chrome_options=chrome_options) '''在browser对应的浏览器中,以禁止图片加载的方式访问百度首页''' browser.get('http://www.baidu.com') '''查看当前浏览器中已设置的参数''' chrome_options.experimental_options
能够看出,在进行如上设置后,咱们访问的网页中全部图片都没有加载,这在不须要采集图片资源的任务中,对于提高访问速度有着重要意义;
2.设置代理IP
有些时候,在面对一些对访问频率有所限制的网站时,一旦咱们爬取频率太高,就会致使咱们本机的IP地址遭受短暂的封禁,这时咱们能够经过收集一些IP代理来创建咱们的代理池,关于这一点咱们会在以后单独开一篇博客来详细介绍,下面简单演示一下如何为咱们的Chrome()浏览器对象设置IP代理:
from selenium import webdriver '''设置代理IP''' IP = '106.75.9.39:8080' '''为Chrome浏览器配置chrome_options选项''' chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--proxy-server=http://{}'.format(IP)) '''将配置好的chrome_options选项传入新的Chrome浏览器对象中''' browser = webdriver.Chrome(chrome_options=chrome_options) '''尝试访问百度首页''' browser.get('http://www.baidu.com')
可是若是你不是付费购买的高速IP代理,而是从网上所谓的免费IP代理网站扒下来的一些IP地址,那么上述设置以后打开的浏览器中不必定能在正常时间内显示目标网页(缘由你懂的);
另外一种思路:
除了使用ChromeOptions()中的方法来设置,还有一种简单直接粗暴的方法,咱们能够直接访问对应当前浏览器设置页面的地址:chrome://settings/content:
from selenium import webdriver browser = webdriver.Chrome() '''直接访问设置页面''' browser.get('chrome://settings/content')
接着再使用本身编写的模拟点击规则,便可完成对应的设置内容,这里便再也不多说;
3.2 浏览器运行时的实用方法
通过了3.1中介绍的方式,对浏览器进行预配置,并成功打开对应的浏览器以后,selenium中还存在着很是丰富的浏览器方法,下面咱们就其中实用且经常使用的一些方法和类内的变量进行介绍:
假设咱们构造了一个叫作browser的浏览器对象,可使用的方法以下:
browser.get(url):在浏览器主窗口中打开url指定的网页;
browser.title:得到当前浏览器中主页面的网页标题:
from selenium import webdriver browser = webdriver.Chrome() '''直接访问设置页面''' browser.get('https://hao.360.cn/?wd_xp1') '''打印网页标题''' print(browser.title)
browser.current_url:返回当前主页面url地址
browser.page_source:获取当前主界面的页面内容,至关于requests.get(url).content
browser.close():关闭当前主页面对应的网页
browser.quit():直接关闭当前浏览器
browser.maximize_window():将浏览器窗口大小最大化
browser.fullscreen_window():将浏览器窗口全屏化
browser.back():控制当前主页面进行后退操做(前提是它有上一页面)
browser.forward():控制当前主页面进行前进操做(前提是它有下一页面)
browser.refresh():控制当前主页面进行刷新操做
browser.set_page_load_timeout(time_to_wait):为当前浏览器设置一个最大页面加载耗时容忍阈值,单位秒,相似urllib.urlopen()中的timeout参数,即当加载某个界面时,持续time_to_wait秒还未加载完成时,程序会报错,咱们能够利用错误处理机制捕捉这个错误,此方法适用于长时间采样中某个界面访问超时假死的状况
browser.set_window_size(width, height, windowHandle='current'):用于调节浏览器界面长宽大小
关于主页面:
这里要额外介绍一下,咱们在前面一大段中提到过不少次主页面这个概念,是由于在selenium控制浏览器时,不管浏览器中开了多少个网页,都只将惟一一个网页视为主页面,相应的不少webdriver()方法也都是以该主页面为对象,下面是一个示例,咱们以马蜂窝地方游记页面为例:
from selenium import webdriver browser = webdriver.Chrome() '''访问马蜂窝重庆游记汇总页''' browser.get('http://www.mafengwo.cn/search/s.php?q=%E9%87%8D%E5%BA%86&t=info&seid=71F18E8D-AA90-4870-9928-2BE01E53DDBD&mxid=&mid=&mname=&kt=1')
打开目标页面以下:
这里咱们手动点开一篇游记(模拟点击的方法下文会介绍),浏览器随即跳转到一个新的页面:
这时咱们运行下列代码:
'''打印网页标题''' print(browser.title)
能够看到,虽然在咱们的视角里,经过点击,进入到一个新的界面,但当咱们利用对应方法获取当前页面标题时,仍然是以以前的页面做为对象,这就涉及到咱们以前提到的主页面的问题,当在原始页面中,由于点击事件而跳转到另外一个页面(这里指的是新开一个窗口显示新界面,而不是在原来的窗口覆盖掉原页面),浏览器中的主页面依旧是锁定在原始页面中,即get()方法跳转到的网页,这种状况咱们就须要用到网页的句柄来惟一标识每个网页;
在selenium中,关于获取网页句柄,有如下两个方法:
browser.current_window_handle:获取主页面的句柄,以上面马蜂窝的为例:
'''打印主页面句柄''' print(browser.current_window_handle)
browser.window_handles:获取当前浏览器中全部页面的句柄,按照打开的时间顺序:
'''打印当前浏览器下全部页面的句柄''' print(browser.window_handles)
既然句柄至关于网页的身份证,那么咱们能够基于句柄切换当前的主网页到其余网页之上,延续上面的例子,此时的主网页是.get()方法打开的网页,以前打印browser.title也是指向的该网页,如今咱们利用browser.switch_to.window(handle)方法,将主网页转到最近打开的网页中,并打印当前主网页的标题:
'''切换主网页至最近打开的网页''' browser.switch_to.window(browser.window_handles[-1]) '''打印当前主网页的网页标题''' print(browser.title)
能够看到,使用主网页切换方法后,咱们的主网页转到指定的网页中,这在对特殊的网页跳转方式下新开的网页内容的采集很受用;
3.3 页面元素定位
在介绍selenium的精髓——模拟浏览器行为以前,咱们须要知道如何对网页内的元素进行定位,譬如说咱们要想定位到网页中的翻页按钮,就须要对翻页按钮所在的位置进行定位,这里的定位不是指在屏幕的平面坐标上进行定位,而是基于网页自身的CSS结构,其实selenium中对网页元素进行定位的方式很是多,可是经过我大量的实践,其中不少方法效果并不尽如人意,惟有其中基于xpath的定位方法十分方便,定位很是准确方便,所以本文不会浪费你的时间介绍其余效果不太好的方法,直接介绍基于xpath的定位方法,咱们先了解一下什么是xpath:
关于xpath:
xpath是一门在xml文档中查找信息的语言,只是为了在selenium中定位网页元素的话,咱们只须要掌握xpath路径表达式便可;
xpath使用路径表达式来识别xml文档中的节点或节点集,咱们先从一个示例出发来对xpath路径表达式有一个认识:
仍是以马蜂窝游记页面为例:
from selenium import webdriver browser = webdriver.Chrome() '''访问马蜂窝重庆游记汇总页''' browser.get('http://www.mafengwo.cn/search/s.php?q=%E9%87%8D%E5%BA%86&t=info&seid=71F18E8D-AA90-4870-9928-2BE01E53DDBD&mxid=&mid=&mname=&kt=1')
经过浏览器的开发者工具,咱们找到“下一页”按钮元素在CSS结构中所在的位置:
先把该元素完整的xpath路径表达式写出来:
//div/div/a[@class='ti next _j_pageitem']
接着咱们使用基于xpath的定位方法,定位按钮的位置并模拟点击:
'''定位翻页按钮的位置并保存在新变量中''' ChagePageElement = browser.find_element_by_xpath("//div/div/a[@class='ti next _j_pageitem']") '''对按钮位置变量使用click方法进行模拟点击''' ChagePageElement.click()
上述代码运行以后,咱们的浏览器执行了对翻页按钮的模拟点击,实现了翻页:
如今咱们来介绍一下xpath路径表达式中的一些基本知识:
nodename:标明一个结点的标签名称
/:父节点与子节点之间的分隔符
//:表明父节点与下属某个节点之间若干个中间节点
[]:指定最末端结点的属性
@:在[]中指定属性名称和对应的属性值
在xpath路径表达式中还有不少其余内容,但在selenium中进行基本的元素定位了解到上面这些规则就能够了,因此咱们上面的例子中的规则,表示的就是定位
若干节点-<div>
... ...
<div>
... ...
<a class='ti next _j_pageitem'></a>
... ...
<div>
... ...
</div>
利用这样的方式,基于browser.find_element_by_xpath()和browser.find_elements_by_xpath(),咱们就能够找到页面中单个独特元素或多个同类型元素,再使用.click()方法便可完成对页面内任意元素的模拟点击;
3.4 基础的浏览器动做模拟
除了上面一小节介绍的使用元素.click()控制点击动做之外,selenium还支持丰富多样的其余常见动做,由于本文是我介绍selenium的上篇,下面只介绍两个经常使用的动做,更复杂的组合动做放在以后的文章中介绍:
模拟网页下滑:
不少时候咱们会遇到这样的动态加载的网页,如光点壁纸的各个壁纸板块,这里以风景板块为例http://pic.adesk.com/cate/landscape:
这个网页的特色是,大多数状况下没有翻页按钮,而是须要用户将页面滑到底部以后,才会自动加载下一页的内容,而且这种机制持续固定几回后,会参杂一个必须点击才能够进行翻页操做的按钮,咱们能够在selenium中使用browser.execute_script()方法来传入JavaScript脚原本执行浏览器动做,进而实现下滑功能;
对应下滑到底的JavaScript脚本为'window.scrollTo(0, document.body.scrollHeight)',咱们用下面这段代码来实现持续下滑,并及时捕捉翻页按钮进行点击(利用错误处理机制来实现):
from selenium import webdriver import time browser = webdriver.Chrome() '''访问光点壁纸风景板块页面''' browser.get('http://pic.adesk.com/cate/landscape') '''这里尝试的时候不要循环太屡次,快速加载图片比较吃网速和内存''' for i in range(1, 20): '''这里使用一个错误处理机制, 若是有定位到加载下一页按钮就进行 点击下一页动做,不然继续每隔1秒,下滑到底''' try: '''定位加载下一页按钮''' LoadMoreElement = browser.find_element_by_xpath("//div/div[@class='loadmore']") LoadMoreElement.click() except Exception as e: '''浏览器执行下滑动做''' browser.execute_script('window.scrollTo(0, document.body.scrollHeight)') time.sleep(1)
模拟输入:
有些时候,咱们须要对界面中出现的输入框,即标签为<input></input>表明的对象进行模拟输入操做,这时候咱们只须要对输入框对应的网页对象进行定位,而后使用browser.send_keys(输入内容)来往输入框中添加文本信息便可,下面是一个简单的例子,咱们从百度首页出发,模拟了点击登录-点击注册-在用户名输入框中输入指定的文本内容,这样一个简单的过程:
from selenium import webdriver browser = webdriver.Chrome() '''访问百度首页''' browser.get('http://www.baidu.com') '''对页面右上角的登录超连接进行定位,这里由于同名超连接有两个, 因此使用find_elements_by_xpath来捕获一个元素列表,再对其中 咱们指定的对象执行点击操做''' LoginElement = browser.find_elements_by_xpath("//a[@name='tj_login']") '''对指定元素进行点击操做''' LoginElement[1].click() '''这段while语句是为了防止信息块没加载完成致使出错''' while True: try: '''捕获弹出的信息块中的注册按钮元素''' SignUpElement = browser.find_elements_by_xpath("//a[@class='pass-reglink pass-link']") '''点击弹出的信息块中的注册超连接''' SignUpElement[0].click() break except Exception as e: pass '''将主网页切换至新弹出的注册页面中以便对其页面内元素进行定位''' browser.switch_to.window(browser.window_handles[-1]) while True: try: '''对用户名称输入框对应元素进行定位''' InputElement = browser.find_element_by_xpath("//input[@name='userName']") '''模拟输入指定的文本信息''' InputElement.send_keys('Keras') break except Exception as e: pass
以上就是关于selenium进行网络数据采集的上篇内容,其他的内容我会挤出时间继续整理介绍,敬请关注,若有笔误,望指出。