ItemSpider中被收集之后,它将会被传递到 Item Pipeline,一些组件会按照一定的顺序执行对 Item 的处理

每个 item pipeline 组件(有时称之为Item Pipeline)是实现了简单方法的 Python 类。他们接收到Item并通过它执行一些行为,同时也决定此Item是否继续通过 pipeline,或是被丢弃而不再进行处理。

以下是item pipeline的一些典型应用:

1.编写你自己的item pipeline

编写你自己的item pipeline很简单,每个item pipeline组件是一个独立的 Python 类,同时必须实现以下方法:

方法描述
process_item(self, item, spider)每个item pipeline组件都需要调用该方法,这个方法必须返回一个Item(或任何继承类)对象, 或是抛出DropItem异常,被丢弃的 item将不会被之后的 pipeline 组件所处理
open_spider(self, spider)spider 被开启时,这个方法被调用
close_spider(spider)spider被关闭时,这个方法被调用
from_crawler(cls, crawler)如果存在,则调用该类方法从Crawler创建管道实例。它必须返回管道的新实例。crawler对象提供对所有Scrapy核心组件(如设置和信号)的访问;这是管道访问它们并将其功能挂钩到Scrapy中的一种方式

2.Item pipeline示例

验证价格,同时丢弃没有价格的 item

让我们来看一下以下这个假设的 pipeline,它为那些不含税(price_excludes_vat 属性)的item调整了 price 属性,同时丢弃了那些没有价格的 item:

from scrapy.exceptions import DropItem

class PricePipeline(object):

    vat_factor = 1.15

    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)

将 item 写入 JSON 文件

以下 pipeline 将所有(从所有spider中)爬取到的 item,存储到一个独立地items.jl文件,每行包含一个序列化为JSON 格式的item

import json

class JsonWriterPipeline(object):

    def __init__(self):
        self.file = open('items.jl', 'wb')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

JsonWriterPipeline 的目的只是为了介绍怎样编写 item pipeline,如果你想要将所有爬取的 item 都保存到同一个 JSON 文件, 你需要使用 Feed exports

将item写入MongoDB

在此示例中,我们将使用pymongo将item写入MongoDB。在Scrapy设置中指定了MongoDB地址数据库名称;MongoDB集合以项类命名。

该示例的主要目的是演示如何使用from_crawler()方法以及如何正确清理资源。

import pymongo

class MongoPipeline(object):

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert_one(dict(item))
        return item

Take screenshot of item

import scrapy
import hashlib
from urllib.parse import quote


class ScreenshotPipeline(object):
    """Pipeline that uses Splash to render screenshot of
    every Scrapy item."""

    SPLASH_URL = "http://localhost:8050/render.png?url={}"

    def process_item(self, item, spider):
        encoded_item_url = quote(item["url"])
        screenshot_url = self.SPLASH_URL.format(encoded_item_url)
        request = scrapy.Request(screenshot_url)
        dfd = spider.crawler.engine.download(request, spider)
        dfd.addBoth(self.return_item, item)
        return dfd

    def return_item(self, response, item):
        if response.status != 200:
            # Error happened, return item.
            return item

        # Save screenshot to file, filename will be hash of url.
        url = item["url"]
        url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
        filename = "{}.png".format(url_hash)
        with open(filename, "wb") as f:
            f.write(response.body)

        # Store filename in item.
        item["screenshot_filename"] = filename
        return item

去重

一个用于去重的过滤器,丢弃那些已经被处理过的item。让我们假设我们的item有一个唯一的id,但是我们 spider 返回的多个 item 中包含有相同的 id:

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

启用一个 Item Pipeline 组件

为了启用一个Item Pipeline组件,你必须将它的类添加到ITEM_PIPELINES配置,就像下面这个例子:

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

数值越小优先级越高,通常将这些数字定义在 0-1000 范围内。

Scrapy 1.7

Scrapy 安装并创建运行爬虫项目(1) Scrapy 命令行工具(2) Scrapy 选择器(3) Scrapy Item(4) Scrapy Item Loaders(5) Scrapy Item Pipeline(6) Scrapy Feed exports(7) Scrapy 架构概览(8) Scrapy 手写一个爬虫(9) Scrapy 知乎模拟登陆 (10) Scrapy源码分析 架构概览(1) Scrapy源码分析 运行入口(2) Scrapy源码分析 核心组件初始化(3) Scrapy源码分析 核心抓取流程(4)