3.3 使用爬虫框架Scrapy
因为爬虫应用程序的需求日益高涨,所以在市面中诞生了很多第三方开源爬虫框架,其中Scrapy是一个为了爬取网站数据、提取结构性数据而编写的专业框架。Scrapy框架的用途十分广泛,可以用于数据挖掘、数据监测和自动化测试等工作。本节将简要讲解爬虫框架Scrapy的基本用法。
3.3.1 Scrapy框架基础
框架Scrapy使用了Twisted异步网络库来处理网络通信,其整体架构大致如图3-9所示。
图3-9 框架Scrapy的架构
在Scrapy框架中,主要包括如下所示的组件。
● 引擎(Scrapy Engine):来处理整个系统的数据流,会触发框架核心事务。
● 调度器(Scheduler):用来获取Scrapy发送过来的请求,然后将请求传入队列中,并在引擎再次请求的时候返回。调度器的功能是设置下一个要抓取的网址,并删除重复的网址。
● 下载器(Downloader):建立在高效的异步模型Twisted之上,下载目标网址中的网页内容,并将网页内容返回给Scrapy Engine。
● 爬虫(Spiders):功能是从特定的网页中提取指定的信息,这些信息在爬虫领域中被称为实体(Item)。
● 项目管道(Pipeline):处理从网页中提取的爬虫实体。当使用爬虫解析一个页面后,会将实体发送到项目管道中进行处理,然后验证实体的有效性,并将不需要的信息删除。
● 下载器中间件(Downloader Middlewares):此模块位于Scrapy引擎和Downloader之间,为Scrapy引擎与下载器之间的请求及响应建立桥梁。
● 爬虫中间件(Spider Middlewares):此模块在Scrapy引擎和Spiders之间,功能是处理爬虫的响应输入和请求输出。
● 调度中间件(Scheduler Middewares):在Scrapy引擎和Scheduler之间,表示从Scrapy引擎发送到调度的请求和响应。
在使用Scrapy框架后,下面是大多数爬虫程序的运行流程。
1)Scrapy Engine从调度器中取出一个URL链接,这个链接作为下一个要抓取的目标。
2)Scrapy Engine将目标URL封装成一个Request请求并传递给下载器,下载器在下载URL资源后,将资源封装成Response应答包。
3)使用爬虫解析Response应答包,如果解析出Item实体,则将结果交给实体管道进行进一步的处理。如果是URL链接,则把URL交给Scheduler等待下一步的抓取操作。
3.3.2 搭建Scrapy环境
在本地计算机安装Python后,可以使用pip命令或easy_install命令来安装Scrapy,具体命令格式如下所示。
另外,需要确保已安装了“win32api”模块,同时必须安装与本地Python版本相对应的版本和位数(32位或64位)。读者可以登录http://www.lfd.uci.edu/~gohlke/pythonlibs/找到需要的版本,如图3-10所示。
图3-10 下载“win32api”模块
下载后将得到一个“.whl”格式的文件,定位到此文件的目录,然后通过如下命令安装“win32api”模块。
注意:如果遇到“ImportError: DLL load failed: 找不到指定的模块。”错误,需要将“Python\Python35\Lib\site-packages\win32”目录中的如下文件保存到本地系统盘中的“Windows\System32”目录下。
● pythoncom36.dll。
● pywintypes36.dll。
3.3.3 创建第一个Scrapy项目
下面的实例代码演示了创建第一个Scrapy项目的过程。
源码路径:daima\3\3-5\
(1)创建项目
在开始爬取数据之前,必须先创建一个新的Scrapy项目。进入准备存储代码的目录中,然后运行如下所示的命令。
上述命令的功能是创建一个包含下列内容的“tutorial”目录。
对上述文件的具体说明如下所示。
● scrapy.cfg:项目的配置文件。
● tutorial/:该项目的python模块,之后在此加入代码。
● tutorial/items.py:项目中的item文件。
● tutorial/pipelines.py:项目中的pipelines文件。
● tutorial/settings.py:项目的设置文件。
● tutorial/spiders/:放置spider代码的目录。
(2)定义Item
Item是保存爬取到的数据的容器,使用方法与Python字典类似,并且提供额外保护机制,避免拼写错误导致未定义的字段错误。在实际应用中可以创建一个scrapy.Item类,并且定义类型为scrapy.Field。
首先需要从dmoz.org获取数据对item进行建模。需要从dmoz中获取名称、url以及网站的描述。对此,在Item中定义相应的字段。编辑“tutorial”目录中的文件items.py,具体实现代码如下所示。
通过定义Item,可以很方便地使用Scrapy中的其他方法。而这些方法需要知道Item的定义。
(3)编写第一个爬虫(Spider)
Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类,其中包含一个用于下载的初始URL,如何跟进网页中的链接和如何分析页面中的内容以及提取生成Item的方法。为了创建一个Spider,必须继承类scrapy.Spider,且定义如下所示的三个属性。
● name:用于区别Spider。该名称必须是唯一的,因此不可以为不同的Spider设定相同的名称。
● start_urls:包含了Spider在启动时进行爬取的url列表。因此,第一个被获取到的页面将是其中之一。后续的URL则从初始的URL获取到的数据中提取。
● parse():spider的一个方法。被调用时,每个初始URL完成下载后生成的Response对象将会作为唯一的参数传递给该方法。它负责解析返回的数据(response data),提取数据(生成Item)以及生成需要进一步处理的URL的Request对象。
下面是我们编写的第一个Spider代码,保存在tutorial/spiders目录下的文件dmoz_spider.py中,具体实现代码如下所示。
(4)爬取
进入项目的根目录,执行下列命令启动spider。
crawl dmoz是负责启动用于爬取dmoz.org网站的Spider,之后得到如下所示的输出。
查看包含dmoz的输出,可以看到在输出的log中包含定义在start_urls的初始URL,并且与spider一一对应。在log中可以看到它没有指向其他页面(referer:None)。同时创建两个包含url所对应的内容的文件:Book和Resources。
由此可见,Scrapy为Spider的start_urls属性中的每个URL创建了scrapy.Request对象,并将Parse方法作为回调函数(callback)赋值给Request。Request对象经过调度,执行生成scrapy.http.Response对象并送回给spider parse()方法。
(5)提取Item
有很多种从网页中提取数据的方法,Scrapy使用了一种基于XPath和CSS表达式机制:Scrapy Selectors。关于Selector和其他提取机制的信息,建议读者参考Selector的官方文档。下面给出XPath表达式的例子及对应的含义:
● /html/head/title:选择HTML文档中<head>标签内的<title>元素。
● /html/head/title/text():选择<title>元素的文字。
● //td:选择所有的<td>元素。
● //div[@class="mine"]:选择所有具有class="mine"属性的div元素。
上面仅仅列出了几个简单的XPath例子,XPath的功能实际上要强大很多。为了配合XPath,Scrapy除了提供Selector之外,还提供了多个方法来避免每次从Response中提取数据时生成Selector的麻烦。
在Selector中有如下4个最基本的方法。
● xpath():用于选取指定的标签内容,例如下面的代码表示选取所有的book标签。
● css():传入CSS表达式,用于选取指定的CSS标签内容。
● extract():返回选中内容的Unicode字符串,返回结果是列表。
● re():根据传入的正则表达式提取数据,返回Unicode字符串格式的列表。
● re_first():返回SelectorList对象中的第一个Selector对象调用的re方法。
使用内置的Scrapy shell,首先需要进入本实例项目的根目录,然后执行如下命令来启动Shell。
此时shell将会输出类似如下所示的内容。
载入Shell后得到一个包含Response数据的本地Response变量。输入“response.body”命令后会输出Response的包体,输入“response.headers”后可以看到Response的包头。更为重要的是,当输入“response.selector”时,将获取一个可以用于查询返回数据的Selector(选择器),以及映射到response.selector.xpath()、response.selector.css()的快捷方法(shortcut):response.xpath()和response.css()。同时,Shell根据Response提前初始化了变量sel。该Selector根据Response的类型自动选择最合适的分析规则(XML vs HTML)。
(6)提取数据
接下来尝试从这些页面中提取有用数据,只需在终端中输入response.body来观察HTML源码并确定合适的XPath表达式。但这个任务非常无聊且不易,可以考虑使用Firefox的Firebug扩展来简化工作。
查看网页源码后会发现网站的信息被包含在第二个<ul>元素中。可以通过下面的代码选择该页面中网站列表里的所有<li>元素。
通过如下命令获取对网站的描述。
通过如下命令获取网站的标题。
3.3.4 抓取某电影网的热门电影信息
本实例的功能是,使用Scrapy爬虫抓取某电影网中热门电影信息。
源码路径:daima\3\3-6\
1)在具体爬取数据之前,必须先创建一个新的Scrapy项目。首先进入准备保存项目代码的目录中,然后运行如下所示的命令。
2)编写文件moviedouban.py设置要爬取的URL范围和过滤规则,主要实现代码如下所示。
3)编写执行脚本文件pyrequests_douban.py,功能是编写功能函数获取热门电影的详细信息,包括电影名、URL链接地址、导演信息、主演信息等。文件pyrequests_douban.py的主要实现代码如下所示。
执行后会输出显示爬取到的热门电影信息,如图3-11所示。
图3-11 爬取到的热门电影信息
3.3.5 抓取某网站中的照片并保存到本地
本实例的功能是使用Scrapy爬虫抓取某网站中的照片信息,并将抓取到的照片保存到本地硬盘中。编写文件art.py设置要爬取的URL范围和抓取的内容元素,主要实现代码如下所示。
源码路径:daima\3\3-7\
执行后会显示抓取目标网站图片的过程,如图3-12所示。
图3-12 网站图片抓取过程
将抓取到的照片保存到本地文件夹“download_images”中,如图3-13所示。
图3-13 在本地硬盘保存抓取到的图片
3.3.6 抓取某网站中的主播照片并保存到本地
本实例的功能是使用Scrapy爬虫抓取某网站中的主播照片,并将抓取到的主播照片保存到本地硬盘中。编写文件douyu.py设置要爬取的URL范围和抓取的内容元素,设置要抓取的Item(是主播昵称和主播照片)。文件douyu.py的主要实现代码如下所示。
源码路径:daima\3\3-8\
执行后会将抓取到的主播照片保存到本地文件夹中,如图3-14所示。
图3-14 抓取到的主播照片