第3章Scrapy爬虫 本章学习目标 了解Scrapy爬虫的概念。 掌握Scrapy爬虫框架的安装。 了解Scrapy爬虫的原理与流程。 掌握Scrapy爬虫框架的实现方式。 本章先向读者介绍Scrapy爬虫的概念,再介绍Scrapy爬虫的原理与流程,最后介绍Scrapy爬虫框架的实现。 视频讲解 3.1Scrapy爬虫概述 1. Scrapy的含义 Scrapy是使用Python语言编写的开源网络爬虫框架,也是一个为了爬取网站数据,提取结构性数据而编写的应用框架。Scrapy 可以应用在数据挖掘、信息处理或存储历史数据等一系列程序中。 Scrapy简单易用、灵活,并且是跨平台的,在Linux及Windows平台上都可以使用。Scrapy框架目前可以支持Python 2.7及Python 3+版本,本章主要介绍在Windows 7中Python 3.7版本下Scrapy框架的应用。 视频讲解 2. Scrapy的安装 在Windows 7中安装Scrapy框架的命令为pip install Scrapy。注意安装Scrapy通常不会直接成功,因为Scrapy框架的安装还需要多个包的支持。 1) 下载Scrapy的twisted包 Scrapy依赖twisted包,同样使用whl格式的包进行安装,进入网站http://www.lfd.uci.edu/~gohlke/pythonlibs/,在网页中搜索twisted,找到其对应的whl包并下载。 2) 下载Scrapy的whl包 在网上输入下载地址“http://www.lfd.uci.edu/~gohlke/pythonlibs/”,进入该网站找到所需要的Scrapy的whl包。 3) 下载Scrapy的lxml包 lxml包是用来做xpath提取的,这个包非常容易安装,直接在命令行窗口中输入“pip install lxml”即可。 4) 下载Scrapy的zope.interface包 zope.interface包是Scrapy爬虫的接口库,可直接在命令行窗口中输入“pip install zope.interface”。 5) 下载Scrapy的pywin32包 pywin32是一个第三方模块库,主要作用是方便Python开发者快速调用Windows API的一个模块库,可直接在命令行中输入“pip install pywin32”。 6) 下载Scrapy的pyOpenSSL包 pyOpenSSL是Python中的一套基于网络通信的协议,可直接在命令行窗口中输入“pip install pyOpenSSL”。 7) 使用命令安装Scrapy 在安装Scrapy框架之前必须依次安装twisted包、whl包、lxml包、zope.interface包、pywin32包和pyOpenSSL包,并在上述包全部安装完成后运行pip install scrapy命令来安装Scrapy框架。为了确保Scrapy框架已经安装成功,可在Python中输入“import scrapy”命令,如果出现图31所示的界面,则表示安装成功。 图31导入Scrapy库 接着再输入“scrapy.version_info”命令显示Scrapy库的版本,运行结果如图32所示。 图32显示Scrapy库的版本 从图32中可以看出,当前安装的版本是1.5.1。在安装完成后,即可使用Scrapy框架爬取网页中的数据。 视频讲解 3.2Scrapy原理 3.2.1Scrapy框架的架构 1. Scrapy架构的组成 Scrapy框架由Engine、Scheduler、Downloader、Spiders、Item Pipeline、Downloader Middlewares及Spider Middlewares等几部分组成,具体结构如图33所示。 图33Scrapy架构的组成 Scrapy框架中具体组件的作用如下。 1) Engine Engine也称为Scrapy引擎,它是爬虫工作的核心,负责控制数据流在系统的所有组件中的流动,并在相应动作发生时触发事件。 2) Scheduler Scheduler也称为调度器,它从Engine接受Request并将它们入队,以便之后Engine请求它们时提供给Engine。 3) Downloader Downloader也称为下载器,它从Internet获取页面数据并提供给Engine,而后提供给Spider。 4) Spiders Spiders也可称为Spider,中文一般译作蜘蛛,它是Scrapy用户编写用于分析由下载器返回的Response,并提取出Item和额外的URL的类,每个Spider都能处理一个域名或一组域名。Spider的整个抓取流程如下: (1) 获取第一个URL的初始请求,当请求返回后调取一个回调函数。第一个请求是通过调用start_requests()方法实现的,该方法默认从start_urls的URL中生成请求,并执行解析来调用回调函数。 (2) 在回调函数中解析网页响应并返回项目对象和请求对象或两者的迭代。这些请求也包含一个回调,然后被Scrapy下载,由指定的回调处理。 (3) 在回调函数中解析网站的内容,使用xpath选择器并生成解析的数据项。 (4) 从Spider返回的项目通常会进驻到项目管道。 5) Item Pipeline Item Pipeline也称为数据管道,它的主要责任是处理由Spider从网页中抽取的数据,主要任务是清洗、验证和存储数据。页面被解析后将被发送到数据管道,并经过几个特定的步骤处理数据。每个数据管道的组件都是由一个简单的方法组成的Python类。Item Pipeline通常的执行过程如下: (1) 清洗HTML数据。 (2) 验证解析到的数据。 (3) 检查数据是否重复。 (4) 将解析到的数据存储到数据库中。 6) Downloader Middlewares Downloader Middlewares也称为下载器中间件,它是介于Scrapy Engine和Downloader之间的中间件,主要用于从Scrapy Engine发送到Downloader的请求和响应。 7) Spider Middlewares Spider Middlewares也称为爬虫中间件,它是介于Scrapy Engine和Spider之间的框架,主要工作是处理Spider的响应输入和请求输出。 在整个框架组成中,Spiders是最核心的组件,Scrapy爬虫开发基本上是围绕Spiders展开的。 此外,在Scrapy框架中还有3种数据流对象,分别是Request、Response和Item。 Request: Scrapy中的HTTP请求对象。 Response: Scrapy中的HTTP响应对象。 Item: 一种简单的容器,用于保存爬取得到的数据。 2. Scrapy框架的工作过程 当Spider要爬取某URL地址的页面时,首先用该URL构造一个Request对象,提交给Engine(图33中的①),随后Request对象进入Scheduler,按照某种调度算法排队,在之后的某个时候从队列中出来,由Engine提交给Downloader(图33中的②、③、④)。Downloader根据Request对象中的URL地址发送一次HTTP请求到目标网站服务器,并接受服务器返回的HTTP响应,构建一个Response对象(图33中的⑤),并由Engine将Response提交给Spider(图33中的⑥),Spider提取Response中的数据,构造出Item对象或者根据新的连接构造出Request对象。分别由Engine提交给Item Pipeline或者Scheduler(图33中的⑦、⑧),这个过程反复进行,直到爬完所有的数据。 3.2.2Request对象和Response对象 Scrapy中的Request对象和Response对象通常用于爬取网站,Request对象在爬虫程序中生成并传递到系统,直到它们到达下载程序,后者执行请求并返回 图34Scrapy中Request对象和 Response对象的作用 一个Response对象,该对象返回到发出请求的爬虫程序。如图34所示的是Scrapy中Request对象和Response对象的作用。 1. Request对象 1) Request基础参数的介绍 Request对象用于描述一个HTTP请求,由Spider产生,Request构造函数的参数列表如下。 Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback]) 参数的含义如下。 url: 请求页面的URL地址。 callback: 请求回来的Response处理函数,也称为回调函数。如果请求没有指定回调函数,将使用spider的parse()方法。 method: HTTP请求的方法,默认为'GET'。 headers: 请求的头部字典,dict类型。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。如果None作为值传递,则不会发送HTTP头。 body: 请求的正文,str或unicode类型。如果unicode传递了a,那么它被编码为str使用传递的编码(默认为UTF8)。如果body没有给出,则存储一个空字符串。不管这个参数的类型是什么,存储的最终值都将是一个str(不会是unicode或None)。 cookies: 设置页面的cookies,dict类型。当某些网站返回cookie(在响应中)时,这些cookie会存储在该域的cookie中,并在将来的请求中再次发送。 meta: 用于在页面之间传递数据,dict类型。Request对象接收一个meta参数,一个字典对象,同时Response对象有一个meta属性可以获取到相应request传过来的meta。 encoding: 请求的编码,url和body参数的默认编码为UTF8。 priority: 请求的优先级(默认为0)。调度器使用优先级来定义用于处理请求的顺序,具有较高优先级的请求将较早执行,允许取负值以指示相对低的优先级。 dont_filter: 表示此请求不应由调度程序过滤,默认为False。 errback: 如果在处理请求时引发任何异常,将调用此函数,包括失败的404 HTTP错误等页面。 2) Request对象方法的介绍 copy(): 复制对象。 replace(): 替换对象。 3) 参数的应用 (1) 将附加数据传递给回调函数。请求的回调函数是当该请求的响应被下载时将被调用的函数。回调函数将使用下载的Request对象作为其第一个参数来调用,代码如下。 def parse_page1(self, response): return scrapy.Request("http://www.example.com/some_page.html", callback=self.parse_page2) def parse_page2(self, response): self.logger.info("Visited %s", response.url) (2) 使用errback在请求处理中捕获异常。请求的errback是在处理异常时被调用的函数,它接收一个Twisted Failure实例作为第一个参数,并可用于跟踪连接超时、DNS错误等,代码如下。 class ErrbackSpider(scrapy.Spider): name = "errback_example" start_urls = [ "http://www.httpbin.org/", "http://www.httpbin.org/status/404", "http://www.httpbin.org/status/500", "http://www.httpbin.org:12345/", "http://www.httphttpbinbin.org/", ] def errback_httpbin(self, failure): self.logger.error(repr(failure)) (3) 使用FormRequest通过HTTP POST发送数据。如果想在爬虫中模拟HTML表单POST并发送几个键值字段,可以返回一个FormRequest对象,代码如下。 return [FormRequest(url="http://www.example.com/post/action", formdata={'name': 'John tom', 'age': '37'}, callback=self.after_post)] 2. Response对象 1) Response基础参数的介绍 Response对象用于描述一个HTTP响应,由Downloader产生,Response构造函数的参数列表如下。 Response(url[, status=200, headers=None, body=b'', flags=None, request=None]) 参数的含义如下。 url: 响应页面的URL地址。 status: 响应的HTTP状态,默认为200。 headers: 包含响应标题的类字典对象。可以使用get()返回具有指定名称的第一个标头值或使用getlist()返回具有指定名称的所有标头值来访问值。 body: HTTP响应正文。 flags: 包含此响应的标志的列表。标志是用于标记响应的标签,例如'cached'、'redirected'等。 request: 产生该HTTP响应的request对象。 2) Response对象方法的介绍 copy(): 返回一个新的响应。 replace(): 返回具有相同成员的Response对象,但通过指定的任何关键字参数赋予新值的成员除外。 3) Response响应子类的介绍 Response对象是一个基类,根据响应内容有3个子类,分别是TextResponse、HtmlResponse和XmlResponse。 (1) TextResponse子类。TextResponse支持新的构造函数,是对Response对象的补充,TextResponse方法的参数如下。 TextResponse(url[, encoding[, ...]]) TextResponse的主要作用是添加一个新的构造函数encoding()。encoding(string)是一个字符串,包含用于此响应的编码。如果创建一个TextResponse具有Unicode主体的对象,它将使用这个编码进行编码。如果encoding()是None(默认值),则将在响应标头和正文中查找编码。 除此以外,TextResponse还支持以下属性或对象。 text: 文本形式的HTTP响应正文。 Selector: 用于在Response中提取数据。在使用时先通过xpath或者css选择器选中页面中要提取的数据,再进行提取。 xpath(query): 使用xpath选择器在Response中提取数据。 css(query): 使用css选择器在Response中提取数据。 urljoin(url): 用于构造绝对URL。 (2) HtmlResponse子类和XmlResponse子类。HtmlResponse和XmlResponse两个类本身只是简单地继承了TextResponse,因此它们是TextResponse的子类。用户通常爬取的网页,其内容大多是HTML文本,创建的就是HtmlResponse类。HtmlResponse类有很多方法,但最常见的是xpath(query)、css(query)和urljoin(url)。其中前两个方法用于提取数据,后一个方法用于构造绝对URL。 3.2.3Select对象 1. Select对象简介 分析Response对象的代码。 def selector(self): from scrapy.selector import Selector if self._cached_selector is None: self._cached_selector = Selector(self) return self._cached_selector def xpath(self, query, **kwargs): return self.selector.xpath(query, **kwargs) 从上面的源代码可以看出,Scrapy的数组组织结构是Selector,它使用xpath选择器在Response中提取数据。 从页面中提取数据的核心技术是HTTP文本解析,在Python中常用的处理模块如下。 BeautifulSoup: 一个非常流行的解析库,API简单,但解析的速度慢。 lxml: 一个使用C语言编写的xml解析库,解析速度快,API相对比较复杂。 Scrapy中的Selector对象是基于lxml库建立的,并且简化了API接口,使用方便。 2. Select对象的用法 在使用Selector对象的时候要先使用xpath或者css选择器选中页面中要提取的数据,然后进行提取。 1) 创建对象 在Python中创建对象有以下两种方式。 (1) 将页面HTML文档字符串传递给Selector构造器的text参数。例如: >>> from scrapy.selector import Selector >>> text = """ <html> <body> <h1>hello world</h1> <h1>hello scrapy</h1> <b>hello python</b> <ul> <li>c++</li> <li>java</li> <li>python<li> </ul> </body> </html> """ >>> selector = Selector(text = text) >>> selector <Selector xpath=None data='<html>\n\t<body>\n\t\t<h1>hello world</h1>\n\t\t'> (2) 使用一个Response对象构造Selector对象。例如: >>> from scrapy.selector import Selector >>> from scrapy.http import HtmlResponse >>> text = """ <html> <body> <h1>hello world</h1> <h1>hello scrapy</h1> <b>hello python</b> <ul> <li>c++</li> <li>java</li> <li>python<li> </ul> </body> </html> """ >>>response=HtmlResponse('url=http://www.example.com',body=text,encoding="utf-8") >>> selector = Selector(response = response) >>> selector <Selector xpath=None data='<html>\n\t<body>\n\t\t<h1>hello world</h1>\n\t\t'> 在实际开发中,一般不需要手动创建Selector对象,在第一次访问一个Response对象的Selector属性时,Response对象内部会以自身为参数自动创建Selector对象,并将Selector对象缓存,以便下次使用。 2) 选中数据 在Scrapy中使用选择器是基于Selector这个对象的,Selector对象在Scrapy中是通过xpath()或css()方法来提取数据的。例如: selector_list = selector.xpath('//h1')#选取文档中所有的h1 selector_list #其中包含两个<h1>对应的Selector对象 <Selector xpath ='.//h1'data='<h1>Hello World</h1>' <Selector xpath ='.//h1'data='<h1>Hello Scrapy</h1>' xpath()和css()方法返回一个SelectorList对象,包含每个被选中部分对应的Selector对象,SelectorList支持列表接口,可以使用for语句迭代访问每一个Selector对象,例如: for sel in Selector_list: print(sel.xpath('/text()')) [<Selector xpath='./text()'data='Hello World'>] [<Selector xpath='./text()'data='Hello Scrapy'>] SelectorList对象也有xpath()和css()方法,调用它们的方法为以接收到的参数分别调用其中一个Selector对象的xpath、css,将所有搜集到的一个新的SelectorList对象返回给用户,例如: selector_list.xpath('./text()') <selector.xpath = './text()'data = 'Hello World'> <selector.xpath = './text()'data = 'Hello Scrapy'> selector.xpath('.//path').css('li').xpath('./text()') [<Selector xpath='.//text()'data='C++'>] [<Selector xpath='.//text()'data='java'>] [<Selector xpath='.//text()'data='python'>] 在具体实现中,Scrapy使用css和xpath选择器来定位元素,它的基本方法如下。 xpath(): 返回选择器列表,每个选择器代表使用xpath语法选择的节点。 css(): 返回选择器列表,每个选择器代表使用css语法选择的节点。 (1) xpath。xpath是XML路径语言,它是一种用来确定XML文档中某部分位置的语言。表31列举了常见的xpath路径表达式。 表31xpath路径表达式 表达式描述 nodename选取次节点的所有子节点 /从根节点选取 //从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 .选取当前节点 ..选取当前节点的父节点 @选取属性 此外,在xpath中可以使用谓语来查找某个特定的节点或者包含某个指定值的节点,谓语被嵌在方括号中; 可以对任意节点使用谓语,并输出结果。表32给出了常见的谓语表达式及输出结果。 表32常见的谓语表达式及输出结果 表达式输 出 结 果 / students/student[1]选取属于students元素的第一个student元素 / students/student[last()]选取属于students元素的最后一个student元素 / students/student[last()-1]选取属于students元素的倒数第二个student元素 / students/student[position()<2]选取最前面的一个属于students元素的student元素 // student[@id]选取所有拥有名为id属性的student元素 // student[@id='00111']选取所有student元素,且这些元素拥有值为00111的id属性 例如: response.xpath('/html/body/div') #选取body下的所有div response.xpath('//a') #选中文档中的所有a response.xpath('/html/body//div') #选中body下的所有节点中的div,无论它在什么位置 response.xpath('//a/text()') #选取所有a的文本 response.xpath('/html/div/*') #选取div的所有元素子节点 【例31】使用Python 3中的lxml库,利用xpath对HTML进行解析。 ① 安装lxml库,命令如下: pip install lxml ② 导入 lxml 库的 etree 模块,命令如下: from lxml import etree 接着声明一段 HTML 文本,调用 HTML 类进行初始化,成功构造一个 XPath 解析对象。该例使用了lxml 的 etree 库,然后利用 etree.HTML 初始化将文件打印出来,如图35所示。 图35构造一个 XPath 解析对象 ③ 查看result的类型,如图36所示。 图36查看result的类型 ④ 使用xpath输出所有li节点,使用xpath输出最后一个节点的内容、倒数第二个节点的内容以及第一个节点的内容,如图37所示。 图37输出对应的节点内容 (2) css。css即层叠样式表,它的语法简单,功能不如xpath强大。当调用Selector对象的css方法时,内部Python库cssSelect将css转化为xpath再执行操作。表33列出了css的一些基本语法。 表33css的基本语法 表达式描述 *选取所有元素 E选取E元素 E1,E2选取E1、E2元素 E1 E2选取E1后代元素中的E2元素 E1>E2选取E1子元素中的E2元素 E1+E2选取兄弟中的E2元素 .container选取class属性为container的元素 # container选取id属性为container的元素 [attr]选取包含attr属性的元素 [attr=value]选取包含attr属性且值为value的元素 E:nth(last)child(n)选取E元素,且该元素必须是其父元素的第n个元素 E:empty选取没有子元素的E元素 E::text选取E元素的文本节点(text node) 例如: response.css('div a::text').extract()#所有div下的所有a的文本 response.css('div a::attr(href)').extract() #href的值 response.css('div>a:nth-child(1)')#选中每个div的第一个a节点,会设定 #只在子节点中找,不会到孙节点中找 response.css('div:not(#container)') #选取所有id不是container的div response.css('div:first-child>a:last-child') #第一个div中的最后一个a 3.2.4Spider开发流程 对于大多数用户来讲,Spider是Scrapy框架中最核心的组件,Scrapy爬虫在开发时通常是紧紧围绕Spider展开的。一般而言,实现一个Spider需要经过以下几步。 (1) 继承scrapy.Spider。 (2) 为Spider命名。 (3) 设置爬虫的起始爬取点。 (4) 实现页面的解析。 1. 继承scrapy.Spider Scrapy框架提供了一个Spider基类,用户编写的Spider都需要继承它,代码如下。 import scrapy class MeijuSpider(scrapy.Spider) scrapy.Spider这个基类实现了以下功能。 (1) 提供了Scrapy引擎调用的接口。 (2) 提供了用户使用的工具函数。 (3) 提供了用户访问的属性。 2. 为Spider命名 在Spider中使用属性name为爬虫命名。该名称在项目中必须是独一无二的,不能和其他爬虫的名称相同。一般是以该网站(domain)来命名Spider。例如用Spider爬取mywebsite.com,该Spider通常会被命名为mywebsite。如果没有给爬虫命名,爬虫会在初始化时抛出ValueError。 3. 设置起始爬取点 start_urls定义Spider开始爬取数据的URL列表,随后生成的其他需要爬取的URL都是从这些URL对应的页面中提取数据生成出来的。例如: start_urls = ['http://www.meijutt.com/new100.html'] 实际上,对于起始爬取点的下载请求是由Scrapy引擎调用Spider对象的start_requests()提交的。这个方法必须返回该Spider的可迭代的初始requests对象供后续爬取。Scrapy会在该Spider开始爬取数据时被调用,且只会被调用一次,因此可以很安全地将start_requests()作为一个生成器来执行。默认的执行会生成每一个在start_urls中的URL对应的Request对象。 4. 实现页面的解析 parse()方法是Scrapy默认的解析函数,用于回调处理下载的response,并且当response没有指定回调函数时,该方法是Scrapy处理response的默认方法。 parse()方法比较简单,只是对response调用_parse_response()方法,并设置callback为“parse_start_url,follow=True”(表明跟进链接)。如果设置了callback,也就是parse_start_url,会优先调用callback处理,然后调用process_results()方法来生成返回列表。例如: def parse(self, response): return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True) Spider中的常见属性和方法如表34所示。 表34Spider中的常见属性和方法 常见属性和方法含义 name定义Spider名称的字符串 allowed_domains包含了Spider允许爬取的域名(domain)的列表,可选 start_urls初始URL元组/列表。当没有指定特定的URL时,Spider将从该列表中开始进行爬取 custom_settings定义该Spider配置的字典,这个配置会在项目范围内运行这个Spider的时候生效 crawler定义Spider实例绑定的crawler对象,这个属性是在初始化Spider类时由from_crawler()方法设置的,crawler对象概括了许多项目的组件 settings运行Spider的配置,这是一个settings对象的实例 logger用Spider名称创建的Python记录器,可以用来发送日志消息 start_requests(self)该方法包含了Spider用于爬取(默认实现是使用start_urls的URL)的第一个Request parse(self, response)当请求URL返回网页没有指定回调函数时,默认的Request对象回调函数,用来处理网页返回的response,以及生成Item或者Request对象 视频讲解 3.3Scrapy的开发与实现 3.3.1Scrapy爬虫的开发流程 要开发Scrapy爬虫,一般有以下几步。 (1) 新建项目。 (2) 确定抓取网页目标。 (3) 制作爬虫。 (4) 设计管道存储爬取内容。 Scrapy爬虫的实现步骤如图38所示。 图38Scrapy爬虫的实现步骤 3.3.2创建Scrapy项目并查看结构 1. Scrapy常用命令介绍 (1) startproject。 startproject命令表示创建一个新工程,所有的爬虫程序都需要先创建新工程,语法为: scrapy startproject <name> 其中name表示新工程的名称。 (2) genspider。 genspider命令表示在该工程下创建一个爬虫,语法为: scrapy genspider <name> <domain> 其中name表示爬虫的名称,domain表示要爬取的网站的域名。 (3) settings。 settings命令表示获取爬虫配置信息,语法为: scrapy settings (4) crawl。 crawl表示运行已经创建好的爬虫,语法为: scrapy crawl <spider> 其中spider表示创建好的爬虫的名称。 (5) list。 list命令表示列出工程中的所有爬虫,语法为: scrapy list 2. 创建Scrapy项目 【例32】创建一个最简单的Spider爬虫。 该例以爬取Spider爬虫专门的训练网站(http://books.toscrape.com/)为例,讲述Scrapy爬虫的创建和运行。该网站首页如图39所示。 图39网站首页 1) 创建工程 在本地选中某一文件夹,同时按住Shift键右击,在弹出的对话框中选择“在此处打开命令窗口”,输入以下命令: scrapy startproject movie 创建Scrapy工程,如图310所示。 图310创建Scrapy工程 2) 创建爬虫程序 在创建好了Scrapy工程以后,下一步就是创建爬虫程序。输入以下命令: scrapy genspider meiju meijutt.com 该命令创建Spider爬虫,并命名为meiju,如图311所示。 图311创建爬虫Spider 3. 查看并认识爬虫目录结构 使用tree命令查看目录结构,如图312所示。 图312目录结构 目录结构含义如下。 scrapy.cfg: 部署Scrapy爬虫的配置文件。 movie/: 外层目录。 items.py: Items代码模板(继承类)。 middlewares.py: middlewares代码模板(继承类)。 pipelines.py: Pipelines代码模板(继承类)。 settings.py: Scrapy爬虫的配置文件。 settings.pyc: Scrapy爬虫的配置文件,由Python生成,同settings.py。 __init__.py: 初始文件,无须修改。 __init__.pyc: 初始化脚本,由Python生成,同__init__.py。 spiders/: Spiders代码模板目录(继承类)。 __init__.py: 初始化脚本。 __init__.pyc: 初始化脚本,同__init__.py。 创建好以后,可在文件夹中查看到如图313~图315所示的目录。 图313根目录结构 图314movie目录结构 图315spiders目录结构 3.3.3编写代码并运行爬虫 1. 设置Spider爬虫 运行Python,在meiju.py中输入以下代码: import scrapy from movie.items import MovieItem class MeijuSpider(scrapy.Spider): name = "meiju" allowed_domains = ["books.com"] start_urls = ['http://books.toscrape.com/catalogue/category/books/travel_2/index.html'] def parse(self, response): movies = response.xpath('//ol[@class="row"]/li') for each_movie in movies: item = MovieItem() item['name'] = each_movie.xpath('article/h3/a/@title').extract()[0] yield item 在Scrapy中使用xpath提取网页中的路径,如图316所示。 图316使用xpath提取网页中的路径 2. 设置item模板 在items.py中输入以下代码: import scrapy class MovieItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() name = scrapy.Field() 3. 设置配置文件 在settings.py中增加以下代码: ITEM_PIPELINES = {'movie.pipelines.MoviePipeline':300} 4. 设置数据处理脚本 在pipelines.py中输入以下代码: import json class MoviePipeline(object): def process_item(self, item, spider): return item 5. 运行爬虫 在爬虫根目录中执行以下命令: scrapy crawl meiju 其中,meiju表示该爬虫Spider的名称,运行结果如图317所示。 图317运行爬虫结果 从图317可以看出,该例要爬取的页面路径为Home/Books/Travel,并通过Spider爬取图书的标题名称,例如title="It′s Only the Himalayas"等,如图318所示。 图318爬取每个标题名称 要爬取的网页URL如下: start_urls = ['http://books.toscrape.com/catalogue/category/books/travel_2/index.html'] 定位网页中的具体爬取位置的相关代码如下所示: movies = response.xpath('//ol[@class="row"]/li') 爬取图书标题的相关代码如下所示: item['name'] = each_movie.xpath('article/h3/a/@title').extract()[0] 如果要将Scrapy爬虫爬取网页的结果保存到文本文件中,可打开pipelinespy,写入以下代码: import json class MoviePipeline(object): def open_spider(self,spider): self.file = open('log.txt', 'w', encoding='utf-8') def close_spider(self,spider): self.file.close() def process_item(self,item,spider): self.file.write(str(item)+'\n') 保存该文件,再运行该爬虫程序,即可将数据写入log.txt中,如图319所示。 图319将数据写入log.txt中 3.4本章小结 (1) Scrapy是使用Python语言编写的开源网络爬虫框架,也是一个为了爬取网站数据,提取结构性数据而编写的应用框架。Scrapy 可以应用在包括数据挖掘、信息处理或存储历史数据等一系列的程序中。 (2) Scrapy框架由Scrapy Engine、Scheduler、Downloader、Spiders、Item Pipeline、Downloader middlewares以及Spider middlewares等几部分组成。 (3) Scrapy中的Request对象和Response对象通常用于爬取网站。Request对象用来描述一个HTTP请求,它常由爬虫生成; 而Response对象一般是由Scrapy自动构建,Response对象有很多属性,可以用来提取网页中的数据。 (4) Spider是Scrapy框架中最核心的组件,Scrapy爬虫在开发时通常是紧紧围绕Spider展开的。 (5) 要开发Scrapy爬虫,一般步骤为新建项目、确定抓取网页目标、制作爬虫、设计管道存储爬取内容。 3.5实训 1. 实训目的 通过本章实训了解Scrapy爬虫框架的特点,能使用Scrapy框架进行简单的网站数据爬取。 2. 实训内容 使用Scrapy框架编写爬虫访问网站。 (1) 访问专门的爬虫网站http://quotes.toscrape.com,该网站有多个页面,例如http://quotes.toscrape.com/page/2/、http://quotes.toscrape.com/page/3/等,首页页面如图320所示。 图320爬虫网站首页 (2) 爬取该页面中的每一个子区域对应的文本内容、作者和分类,如图321所示。 图321爬取网站相应内容 (3) 新建爬虫工程,命名为quotesbotmaster,并在此工程中创建Spider爬虫,命令为toscrapexpath。 (4) 打开toscrapexpath.py,输入以下代码。 import scrapy class ToScrapeSpiderXPath(scrapy.Spider): name = 'toscrape-xpath' start_urls = [ 'http://quotes.toscrape.com/', ] def parse(self, response): for quote in response.xpath('//div[@class="quote"]'): yield { 'text': quote.xpath('./span[@class="text"]/text()').extract_first(), 'author': quote.xpath('.//small[@class="author"]/text()').extract_first(), 'tags': quote.xpath('.//div[@class="tags"]/a[@class="tag"]/text()').extract() } next_page_url = response.xpath('//li[@class="next"]/a/@href').extract_first() if next_page_url is not None: yield scrapy.Request(response.urljoin(next_page_url)) 其中,语句“'text': quote.xpath('./span[@class="text"]/text()').extract_first()”表示爬取页面子区域的文本内容; 语句“'author': quote.xpath('.//small[@class="author"]/text()').extract_first()”表示爬取页面子区域的作者; 语句“'tags': quote.xpath('.//div[@class="tags"]/a[@class="tag"]/text()').extract()”表示爬取页面子区域的分类。 语句“next_page_url = response.xpath('//li[@class="next"]/a/@href').extract_first()”表示依次爬取该网站的下一页。 (5) 在pipelines.py中输入以下代码。 class QuotesbotPipeline(object): def process_item(self, item, spider): return item (6) 在settings.py中输入以下代码。 BOT_NAME = 'quotesbot' SPIDER_MODULES = ['quotesbot.spiders'] NEWSPIDER_MODULE = 'quotesbot.spiders' (7) 在items.py中输入以下代码。 import scrapy class QuotesbotItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() pass (8) 运行该爬虫,在爬虫根目录中执行命令scrapy crawl toscrapexpath,可以得到爬取结果,如图322和图323所示。 图322运行该爬虫 图323爬取的页面内容 (9) 查看爬取的每一个具体内容,如图324所示。 图324爬取的页面具体内容 习题 1. 什么是Scrapy框架? 2. 如何安装Scrapy框架? 3. Scrapy框架有哪些特点? 4. Scrapy框架的组成部分是什么? 5. Scrapy框架的工作原理是什么? 6. 如何使用Scrapy框架来爬取页面?