购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.2 使用Item封装数据

上一节,我们学习了使用Spider从页面中提取数据的方法,并且将提取出来的字段保存于字典中。字典使用虽然方便,但也有它的缺陷:

·字段名拼写容易出错且无法检测到这些错误。

·返回的数据类型无法确保一致性。

·不便于将数据传递给其他组件(如传递给用于数据处理的pipeline组件)。

为了解决上述问题,Scrapy定义了Item类专门用于封装数据。

4.2.1 定义Item和Field

Item对象是一个简单的容器,用于收集抓取到的数据,其提供了类似于字典(dictionary-like)的API,并具有用于声明可用字段的简单语法。

以起点中文网小说热销榜项目qidian_hot为例,在新建项目时,自动生成的items.py文件,就是用于封装数据的。之所以叫items,是因为源文件中可以定义多种Item,其原始代码为:


import scrapy
class QidianHotItem(scrapy.Item):
  # define the fields for your item here like:
  # name = scrapy.Field()
  pass

已知需要爬取的小说的字段有小说名称、作者、类型和形式。在类QidianHotItem中声明这几个字段,代码如下:


import scrapy
#保存小说热销榜字段数据
class QidianHotItem(scrapy.Item):
  # define the fields for your item here like:
  name = scrapy.Field()                                 #小说名称
  author = scrapy.Field()                               #作者
  type = scrapy.Field()                                 #类型
  form = scrapy.Field()                                 #形式

是不是很简单?确实,除了字段名不一样之外,其他的没什么不同。下面分析一下代码:

(1)类QidianHotItem继承于Scrapy的Item类。

(2)name、author、type、form为小说的各个字段名。

(3)scrapy.Field()生成一个Field对象,赋值给各自的字段。

(4)Field对象用于指定每个字段的元数据,并且Field对象对接受的数据没有任何限制。因此,在定义属性字段时,无须考虑它的数据类型,使用起来非常方便。

下面修改HotSalesSpider类中的代码,使用QidianHotItem替代Python字典存储数据,实现代码如下:


#-*-coding:utf-8-*-
from scrapy import Request
from scrapy.spiders import Spider                                       #导入Spider类
from qidian_hot.items import QidianHotItem                      #导入模块
class HotSalesSpider(Spider):
  ……
  # 解析函数
  def qidian_parse(self, response):
      #使用xpath定位到小说内容的div元素,并保存到列表中
      list_selector = response.xpath("//div[@class='book-mid-info']")
      #依次读取每部小说的元素,从中获取小说名称、作者、类型和形式
      for one_selector in list_selector:
          #获取小说名称
          name = one_selector.xpath("h4/a/text()").extract_first()
          #获取作者
          author = one_selector.xpath("p[1]/a[1]/text()").extract()[0]
          #获取类型
          type = one_selector.xpath("p[1]/a[2]/text()").extract()[0]
          #获取形式(连载还是完本)
          form = one_selector.xpath("p[1]/span/text()").extract()[0]
          #将爬取到的一部小说保存到item中
          item = QidianHotItem()                        #定义QidianHotItem对象
          item["name"] = name                           #小说名称
          item["author"] = author                       #作者
          item["type"] = type                           #类型
          item["form"] = form                           #形式
          #使用yield返回item
          yield item
      #获取下一页URL,并生成Request请求提交给引擎
      ……

以上代码分析如下:

(1)首先导入qidian_hot.items下的QidianHotItem模块。

(2)生成QidianHotItem的对象item,用于保存一部小说信息。

(3)将从页面中提取到的各个字段赋给item。赋值方法跟Pyton的字典一样,使用key-value的形式。key要与在QidianHotItem中定义的名称一致,否则会报错,value为各个字段值。Item复制了标准的字典API,因此可以按照字典的形式赋值。

【实用技巧】

有时会有这样的情况,使用Execl打开生成的CSV文件时出现中文乱码。

解决方法:使用记事本打开CSV文件,在“文件”菜单中选择“另存为”命令,“编码格式”选择UTF-8,然后单击“保存”按钮。之后再次使用Excel打开,中文就能正常显示了。

4.2.2 使用ItemLoader填充容器

目前为止我们爬取的数据的字段较少,但是当项目很大、提取的字段数以百计时,数据的提取规则也会越来越多,再加上还要对提取到的数据做转换处理,代码就会变得庞大,维护起来十分困难。

为了解决这个问题,Scrapy提供了项目加载器(ItemLoader)这样一个填充容器。通过填充容器,可以配置Item中各个字段的提取规则,并通过函数分析原始数据,最后对Item字段赋值,使用起来非常便捷。

Item和ItemLoader的区别在于:

·Item提供了保存抓取到的数据的容器,需要手动将数据保存于容器中。

·Itemloader提供的是填充容器的机制。

下面使用ItemLoader来改写起点中文网小说热销榜的项目。打开爬虫(Spider)源文件qidian_hot.py。

1.导入ItemLoader类

导入ItemLoader类,代码如下:


from scrapy.loader import ItemLoader                    #导入ItemLoader类

2.实例化ItemLoader对象

在使用ItemLoader之前,必须先将其实例化。来看一下数据解析函数qidian_parse()的实现代码:


#解析函数
def qidian_parse(self, response):
  #使用xpath定位到小说内容的div元素,并保存到列表中
  list_selector = response.xpath("//div[@class='book-mid-info']")
  #依次读取每部小说的元素,从中获取小说名称、作者、类型和形式
  for one_selector in list_selector:
      #生成ItemLoader的实例
      #参数item接收QidianHotItem实例,selector接收一个选择器
      novel = ItemLoader(item=QidianHotItem(),selector=one_selector)
      #使用XPath选择器获取小说名称
      novel.add_xpath("name","h4/a/text()")
      #使用XPath选择器获取作者
      novel.add_xpath("author","p[1]/a[1]/text()")
      #使用XPath选择器获取类型
      novel.add_xpath("type","p[1]/a[2]/text()")
      #使用CSS选择器获取小说形式(连载还是完本)
      novel.add_css("form",".author span::text")
      #将提取好的数据load出来,并使用yield返回
      yield novel.load_item()

很明显,使用ItemLoader实现的Spider功能,代码量更少、更加清晰。因为ItemLoader将数据提取与数据封装的功能全实现了。

在实例化ItemLoader时,ItemLoader接收一个Item实例来指定要加载的Item(参数item);指定response或者selector来确定要解析的内容(参数response或selector)。

3.使用ItemLoader填充数据

实例化ItemLoader对象后,就要开始提取数据到ItemLoader中了。ItemLoader提供了3种重要的方法将数据填充进来。

·add_xpath():使用XPath选择器提取数据。

·add_css():使用CSS选择器提取数据。

·add_value():直接传值。

以上3个方法中都有两个参数,第一个参数指定字段名,第二个参数指定对应的提取规则或者传值。

在上面的代码中,小说名称、作者、类型都是通过add_xpath填充到ItemLoader实例对象中,而小说形式是通过add_css填充的。add_value用于直接传值,例如:


novel.add_value("form", "连载")                   #字段form值设置为字符串"连载"

4.给Item对象赋值

当提取到的数据被填充到ItemLoader后,还需要调用load_item()方法给Item对象赋值。

5.进一步处理数据

下面两个问题一定也困扰着大家:

(1)使用ItemLoader提取的数据,也是保存于列表中的,以前可以通过extract_first()或者extract()获取列表中的数据,但是在ItemLoader中如何实现呢?以下为生成的数据格式:


{'author': ['傅啸尘'], 'form': ['连载'], 'name': ['修炼狂潮'], 'type': ['玄幻']}

(2)很多时候,我们还需要将选择器(XPath或CSS)提取出来的数据做进一步的处理,例如去除空格、提取数字和格式化数据等。这些处理又在哪里实现呢?

解决方法是,使用输入处理器(input_processor)和输出处理器(output_processor)对数据的输入和输出进行解析。

下面来看一个例子,实现将提取出来的小说形式(连载/完结)转换为简写形式(LZ/WJ)。下面在items.py中实现这个功能,实现代码如下:


import scrapy
from scrapy.loader.processors import TakeFirst
#定义一个转换小说形式的函数
def form_convert(form):
  if form[0] == "连载":
      return "LZ"
  else:
      return "WJ"
#保存小说热销榜字段数据
class QidianHotItem(scrapy.Item):
  #TakeFirst为Scrapy内置处理器,获取列表中第一个非空数据
  name = scrapy.Field(output_processor=TakeFirst())             #小说名称
  author = scrapy.Field(output_processor=TakeFirst())           #作者
  type = scrapy.Field(output_processor=TakeFirst())             #类型
  form = scrapy.Field(input_processor=form_convert,
                      output_processor=TakeFirst())                             #形式

首先定义了一个转换小说形式的函数form_convert( )。参数如果为“连载”,则返回LZ;否则返回WJ。

在QidianHotItem类中,scrapy.Field()中设置了两个参数(或其中之一):输入处理器(input_processor)和输出处理器(output_processor)。

output_processor绑定了函数TakeFirst( )。TakeFirst()函数为Scrapy内置的处理器,用于获取集合中第一个非空值。

form为小说形式的字段,scrapy.Field()的参数input_processor绑定了函数form_convert( )。当ItemLoader通过选择器(XPath或CSS)提取某字段数据后,就会将其发送给输入处理器进行处理,然后将处理完的数据发送给输出处理器做最后一次处理。最后调用load_item()函数将数据填充进ItemLoader,并得到填充后的Item对象。如图4-6所示为每个字段的数据处理过程。

图4-6 字段的处理过程

6.运行项目

通过以下命令运行项目:


>scrapy crawl hot -o hot.csv

打开hot.csv文件,在form列,查看所有的“连载”是否已变成LZ,所有的“完结”是否已变成WJ。

ItemLoader提供了一种灵活、高效和简单的机制,但是,如果爬取的字段不算太多,也没有必要使用它。 g/OoFzI9xbUG2EKaSKNqowGrPEDcQjcfOIaplME382QkZYKS4e2nI3kF/RK1d4iP

点击中间区域
呼出菜单
上一章
目录
下一章
×