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

4.3 使用Pipeline处理数据

前面,我们学习了Scrapy的数据爬取和数据封装,但有时可能还需要对数据进行处理,例如过滤掉重复数据、验证数据的有效性,以及将数据存入数据库等。Scrapy的Item Pipeline(项目管道)是用于处理数据的组件。本节就来学习使用Item Pipeline实现数据的处理的方法。

4.3.1 Item Pipeline介绍

当Spider将收集到的数据封装为Item后,将会被传递到Item Pipeline(项目管道)组件中等待进一步处理。Scrapy犹如一个爬虫流水线,Item Pipeline是流水线的最后一道工序,但它是可选的,默认关闭,使用时需要将它激活。如果需要,可以定义多个Item Pipeline组件,数据会依次访问每个组件,执行相应的数据处理功能。

以下为Item Pipeline的典型应用:

·清理数据。

·验证数据的有效性。

·查重并丢弃。

·将数据按照自定义的格式存储到文件中。

·将数据保存到数据库中。

还是以起点中文网小说热销榜项目qidian_hot为例,来看看如何使用Item Pipeline处理数据。如图4-7所示为获取到的部分小说热销榜数据,字段有名称、作者、类型和形式。其中,小说的形式有连载和完本两种,如果期望小说形式是以首字母简写的形式展现,就可以使用一个Item Pipeline来完成这个功能。下面在项目qidian_hot中实现该功能。

图4-7 部分小说热销榜数据

4.3.2 编写自己的Item Pipeline

编写自己的Item Pipeline组件其实很简单,它只是一个实现了几个简单方法的Python类。当建立一个项目后,这个类就已经自动创建了。打开项目qidian_hot下的pipelines.py,发现自动生成了如下代码:


class QidianHotPipeline(object):
  def process_item(self, item, spider):
      return item

下面在方法process_item()中,实现数据处理的功能。


class QidianHotPipeline(object):
  def process_item(self, item, spider):
      #判断小说形式是连载还是完结
      if item["form"] == "连载":                  #连载的情况
          item["form"] = "LZ"                           #替换为简称
      else:#其他情况
          item["form"] = "WJ"
      return item

QidianHotPipeline是自动生成的Item Pipeline类,它无须继承特定的基类,只需要实现某些特定的方法,如process_item()、open_spider()和close_spider()。注意,方法名不可改变。

process_item()方法是Item Pipeline类的核心方法,必须要实现,用于处理Spider爬取到的每一条数据(Item)。它有两个参数:

item:待处理的Item对象。

spider:爬取此数据的Spider对象。

方法的返回值是处理后的Item对象,返回的数据会传递给下一级的Item Pipeline(如果有)继续处理。

4.3.3 启用Item Pipeline

在Scrapy中,Item Pipeline是可选组件,默认是关闭的。要想激活它,只需在配置文件settings.py中启用被注释掉的代码即可。


# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
 'qidian_hot.pipelines.QidianHotPipeline': 300,
}

ITEM_PIPELINES是一个字典,将想要启用的Item Pipeline添加到这个字典中。其中,键是Item Pipeline类的导入路径,值是一个整数值。如果启用多个Item Pipeline,这些值就决定了它们运行的顺序,数值越小,优先级越高。下面来看一下运行多个Item Pipeline的例子。

4.3.4 多个Item Pipeline

如果有这样一个需求,同一个作者只能上榜一部作品,而爬取到的数据中可能有多部同一作者的作品。因此可以实现一个去重处理的Item Pipeline,将重复数据过滤掉。在pipelines.py中,定义一个去重的Item Pipeline类DuplicatesPipeline,实现代码如下:


from scrapy.exceptions import DropItem
#去除重复作者的Item Pipeline
class DuplicatesPipeline(object):
  def __init__(self):
      #定义一个保存作者姓名的集合
      self.author_set = set()
  def process_item(self, item, spider):
      if item['author'] in self.author_set:
          #抛弃重复的Item项
          raise DropItem("查找到重复姓名的项目: %s"%item)
      else:
          self.author_set.add(item['author'])
      return item

在构造函数__init__()中定义一个保存作者姓名的集合author_set。

在process_item()方法中,判断item中的author字段是否已经存在于集合author_set中,如果不存在,则将item中的author字段存入author_set集合中;如果存在,就是重复数据,使用raise抛出一个DropItem异常,该Item就会被抛弃,不会传递给后面的Item Pipeline继续处理,更不会导出到文件中。

在配置文件settings.py中启用DuplicatesPipeline:


# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
 'qidian_hot.pipelines.DuplicatesPipeline': 100,
 'qidian_hot.pipelines.QidianHotPipeline': 300,
}

DuplicatesPipeline设置的值比QidianHotPipeline小,因此优先执行DuplicatesPipeline,过滤掉重复项,再将非重复项传递给QidianHotPipeline继续处理。

4.3.5 保存为其他类型文件

之前我们都是通过命令将数据保存为CSV文件。但是,如果要将数据保存为TXT文件,并且字段之间使用其他间隔符(例如分号),使用命令就无法实现了。Scrapy中自带的支持导出的数据类型有CSV、JSON和XML等,如果有特殊要求,需要自己实现。

下面通过Item Pipeline实现将数据保存为文本文档(txt),并且字段之间使用分号(;)间隔的功能。在pipelines.py中,定义一个保存数据的Item Pipeline类SaveToTxtPipeline,实现代码如下:


#将数据保存于文本文档中的Item Pipeline
class SaveToTxtPipeline(object):
  file_name = "hot.txt"                         #文件名称
  file = None                                           #文件对象
  #Spider开启时,执行打开文件操作
  def open_spider(self,spider):
      #以追加形式打开文件
      self.file = open(self.file_name,"a",encoding="utf-8")
  #数据处理
  def process_item(self, item, spider):
      #获取item中的各个字段,将其连接成一个字符串
      # 字段之间用分号隔开
      # 字符串末尾要有换行符\n
      novel_str = item['name']+";"+\
                  item["author"]+";"+\
                  item["type"]+";"+\
                  item["form"]+"\n"
      #将字符串写入文件中
      self.file.write(novel_str)
      return item
  #Spider关闭时,执行关闭文件操作
  def close_spider(self,spider):
      #关闭文件
      self.file.close()

类SaveToTxtPipeline中多了几个方法,下面一起来了解一下。

Item Pipeline中,除了必须实现的process_item()方法外,还有3个比较常用的方法,可根据需求选择实现。

·open_spider(self,spider)方法:当Spider开启时(爬取数据之前),该方法被调用,参数spider为被开启的Spider。该方法通常用于在数据处理之前完成某些初始化工作,如打开文件和连接数据库等。

·close_spider(self,spider)方法:当Spider被关闭时(所有数据被爬取完毕),该方法被调用,参数spider为被关闭的Spider。该方法通常用于在数据处理完后,完成某些清理工作,如关闭文件和关闭数据库等。

·from_crawler(cls,crawler)方法:该方法被调用时,会创建一个新的Item Pipeline对象,参数crawler为使用当前管道的项目。该方法通常用于提供对Scrapy核心组件的访问,如访问项目设置文件settings.py。

下面再来看一下SaveToTxtPipeline实现的思路。

(1)属性file_name定义文件名称,属性file定义文件对象,便于操作文件。

(2)open_spider()方法实现文件的打开操作,在数据处理前执行一次。

(3)close_spider()方法实现文件的关闭操作。在数据处理后执行一次。

(4)process_item()方法实现数据的写入操作。首先,获取item中的所有字段,将它们连成字符串,字段之间用分号间隔,而且字符串末尾要加上换行符(\n),实现一行显示一条数据;然后,使用Python的write()函数将数据写入文件中。

在配置文件settings.py中启用SaveToTxtPipeline:


# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
 'qidian_hot.pipelines.DuplicatesPipeline': 100,
 'qidian_hot.pipelines.QidianHotPipeline': 300,
 'qidian_hot.pipelines.SaveToTxtPipeline': 400,
}

运行爬虫程序,查看hot.txt文件中的内容,如图4-8所示。

图4-8 生成的文本文档

为了便于管理,Scrapy中将各种配置信息放在了配置文件settings.py中。上面的文件名也可以转移到settings.py中配置,下面来看一下修改方法。

(1)在settings.py中设置文件名称。


FILE_NAME = "hot.txt"

(2)在SaveToTxtPipeline中获取配置信息。


#将数据保存于文本文档中的Item Pipeline中
class SaveToTxtPipeline(object):
  file = None              #文件对象
  @classmethod
  def from_crawler(cls,crawler):
      #获取配置文件中的FILE_NAME的值
      #如果获取失败,就使用默认值"hot2.txt"
      cls.file_name = crawler.settings.get("FILE_NAME","hot2.txt")
      return cls()
……

SaveToTxtPipeline增加了方法from_crawler(),用于获取配置文件中的文件名。注意,该方法是一个类方法(@classmethod),Scrapy会调用该方法来创建SaveToTxtPipeline对象,它有两个参数:

·cls:SaveToTxtPipeline对象。

·crawler:Scrapy中的核心对象,通过它可以访问配置文件(settings.py)。

from_crawler()方法必须返回一个新生成的SaveToTxtPipeline对象(return cls())。 xX5J8nDsIO9egRI03LE1+oOWZA13O4M4QOunLPpr2q1LSr9PriVShpeaqKpQ4dAE

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

打开