アイテム

スクレイピングの主な目標は、非構造化ソース(通常はWebページ)から構造化データを抽出することです。 スパイダー は、抽出されたデータを アイテム 、つまりkey-valueペアを定義するPythonオブジェクトとして返す場合があります。

Scrapyは 複数のタイプのアイテム をサポートしています。 あなたがアイテムを作成したいときは、任意のタイプのアイテムを使用できます。 アイテムを受け取るコードを作成する場合、そのコードは 任意のアイテムタイプ で機能する必要があります。

アイテム型

Scrapyは、 itemadapter ライブラリを介して以下の型のアイテムをサポートします: 辞書Item オブジェクトdataclass オブジェクトattrs オブジェクト

辞書

アイテム型として辞書( dict )は便利でおなじみです。

Itemオブジェクト

Itemdict 風のAPIに加えて、アイテム型としての豊富な機能を提供します:

class scrapy.item.Item([arg])[ソース]

Item オブジェクトは、__init__ メッソドを含む標準の dict APIを複製(replicate)します。

Item ではフィールド名を定義できるため、以下のようになります:

  • KeyError は、未定義のフィールド名を使用すると発生(raise)します(つまり、タイプミスが見過ごされないようにします)

  • アイテム・エクスポーター は、最初にスクレイプされたオブジェクトにすべてのフィールドの値が揃ってない場合でも、デフォルト値をセットしてすべてのフィールドをエクスポートできます

Item はフィールド・メタ・データを定義することもできます。これは シリアライズのカスタマイズ に使用できます。

trackref は、 Item を追跡しメモリ・リークを見つけるのに役立ちます(see trackref を使用したメモリ・リークのデバッグ)。

Item オブジェクトは、以下の追加APIメンバーも提供します:

copy()
deepcopy()

このアイテムの deepcopy() を返す。

fields

読み込まれたフィールドだけでなく、このアイテムの すべての宣言済みフィールド を含む辞書。 キーはフィールド名で、値は アイテム宣言 で使用される Field オブジェクトです。

例:

from scrapy.item import Item, Field

class CustomItem(Item):
    one_field = Field()
    another_field = Field()

Dataclassオブジェクト

バージョン 2.2 で追加.

dataclass() を使用すると、フィールド名を使用してアイテム・クラスを定義できるため、 アイテム・エクスポーター は、最初にスクレイプされたオブジェクトにすべての値がない場合でも、デフォルトでそれらすべてのフィールドをエクスポートできます。

さらに、 dataclass アイテムは以下も可能にします:

これらはPython3.7以降はネイティブに機能します。また、Python3.6では dataclasses backport を使用します。

例:

from dataclasses import dataclass

@dataclass
class CustomItem:
    one_field: str
    another_field: int

注釈

フィールド型は実行時に強要されません。

attr.sオブジェクト

バージョン 2.2 で追加.

attr.s() を使用すると、フィールド名を使用してアイテム・クラスを定義できるため、 アイテム・エクスポーター は、最初にスクレイプされたオブジェクトにすべてのフィールドの値がない場合でも、デフォルトですべてのフィールドをエクスポートできます。

さらに、 attr.s アイテムは以下も可能にします:

この型を使用するには、 attrsパッケージ をインストールする必要があります。

例:

import attr

@attr.s
class CustomItem:
    one_field = attr.ib()
    another_field = attr.ib()

Itemオブジェクトで作業する

Itemサブクラス宣言

Itemsサブクラスは、単純なクラス定義構文と Field オブジェクトを使用して宣言します。 以下に例を示します:

import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    tags = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

注釈

Django に精通している人は、Scrapyアイテムが Django Models と同様に宣言されていることに気付くでしょう。ただし、異なるフィールド型の概念がないため、Scrapyアイテムははるかに単純です。

フィールド宣言

Field オブジェクトは、各フィールドのメタ・データを指定するために使用されます。 たとえば、上記の例で示した last_updated フィールドのシリアル化関数です。

あなたは各フィールドに任意の種類のメタ・データを指定できます。 Field オブジェクトが受け入れる値には制限はありません。これと同じ理由で、利用可能なすべてのメタ・データ・キーの参照リストはありません。 Field オブジェクトで定義された各キーは異なるコンポーネントで使用でき、それらのコンポーネントのみがそれについて知っています。プロジェクトで他の Field キーを定義して使用することもできます。 Field オブジェクトの主な目的は、すべてのフィールド・メタ・データを1か所で定義する方法を提供することです。 通常、各フィールドに動作が依存するコンポーネントは、特定のフィールド・キーを使用してその動作を構成します。 各コンポーネントで使用されているメタ・データ・キーを確認するには、ドキュメントを参照する必要があります。

アイテムの宣言に使用される Field オブジェクトは、クラス属性として割り当てられたままにならないことに注意することが重要です。 代わりに、 Item.fields 属性を介してアクセスできます。

class scrapy.item.Field([arg])[ソース]

Field クラスは組み込みの dict クラスの単なるエイリアスであり、追加の機能や属性を提供しません。 言い換えれば、 Field オブジェクトは昔ながらのPython辞書です。 別のクラスを使用して、クラス属性に基づいて アイテム宣言構文 をサポートします。

注釈

metadataフィールドは、 dataclassattrs アイテムに対しても宣言できます。 詳細については、 dataclasses.fieldattr.ib の文書を参照してください。

Itemオブジェクトで作業する

ここで、 先程宣言した Product アイテムを使って、アイテムで実行される一般的なタスクの例をいくつか示します。 APIは dict APIに非常に似ていることに気付くでしょう。

アイテムの作成

>>> product = Product(name='Desktop PC', price=1000)
>>> print(product)
Product(name='Desktop PC', price=1000)

フィールド値の取得

>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC
>>> product['price']
1000
>>> product['last_updated']
Traceback (most recent call last):
    ...
KeyError: 'last_updated'
>>> product.get('last_updated', 'not set')
not set
>>> product['lala'] # getting unknown field
Traceback (most recent call last):
    ...
KeyError: 'lala'
>>> product.get('lala', 'unknown field')
'unknown field'
>>> 'name' in product  # is name field populated?
True
>>> 'last_updated' in product  # is last_updated populated?
False
>>> 'last_updated' in product.fields  # is last_updated a declared field?
True
>>> 'lala' in product.fields  # is lala a declared field?
False

フィールド値のセット

>>> product['last_updated'] = 'today'
>>> product['last_updated']
today
>>> product['lala'] = 'test' # setting unknown field
Traceback (most recent call last):
    ...
KeyError: 'Product does not support field: lala'

読み込まれたすべての値へのアクセス

読み込まれたすべての値にアクセスするには、典型的な dict API を使用するだけです:

>>> product.keys()
['price', 'name']
>>> product.items()
[('price', 1000), ('name', 'Desktop PC')]

アイテムのコピー

アイテムをコピーするには、あなたは最初に浅いコピーとディープ・コピーのどちらを使用するかを決定する必要があります。

アイテムにリストや辞書などのミュータブル( mutable )値が含まれている場合、浅いコピーは、すべての異なるコピー間で同じミュータブル値への参照を保持します。

たとえば、タグのリストを持つアイテムがあり、そのアイテムの浅いコピーを作成する場合、元のアイテムとコピーの両方に同じタグのリストがあります。 アイテムの1つのリストにタグを追加すると、他のアイテムにもタグが追加されます。

それが望ましい振る舞いでない場合は、代わりにディープ・コピーを使用します。

詳細は copy 参照。

アイテムの浅いコピーを作成するには、あなたは、既存のアイテムで copy() を呼び出す(product2 = product.copy())か、あるいは、既存のアイテムからアイテム・クラスをインスタンス化(product2 = Product(product))のいずれかが可能です。

ディープ・コピーを作成するには、代わりに deepcopy() を呼び出します(product2 = product.deepcopy())。

その他の一般的な作業

アイテムから辞書を作成する:

>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}

辞書からアイテムを作成する:

>>> Product({'name': 'Laptop PC', 'price': 1500})
Product(price=1500, name='Laptop PC')
>>> Product({'name': 'Laptop PC', 'lala': 1500}) # warning: unknown field in dict
Traceback (most recent call last):
    ...
KeyError: 'Product does not support field: lala'

Itemサブクラスの拡張

あなたは、元のアイテムのサブクラスを宣言することにより、アイテムを拡張できます(フィールドを追加したり、フィールドのメタ・データを変更したりできます)。

例えば:

class DiscountedProduct(Product):
    discount_percent = scrapy.Field(serializer=str)
    discount_expiration_date = scrapy.Field()

次のように、あなたは、以前のフィールド・メタ・データを使用して値を追加したり、既存の値を変更したりして、フィールド・メタ・データを拡張することもできます:

class SpecificProduct(Product):
    name = scrapy.Field(Product.fields['name'], serializer=my_serializer)

これは、 name フィールドの serializer メタ・データ・キーを追加(または置換)し、以前に存在したすべてのメタ・データ値を保持します。

全アイテム型のサポート

アイテム・パイプライン または スパイダー・ミドルウェア のメソッドなど、アイテムを受け取るコードでは、任意の サポートされているアイテム型 で機能するコードを書くために ItemAdapter クラスと is_item() 関数を使用することをお勧めします。

class itemadapter.ItemAdapter(item: Any)[ソース]

データ・コンテナ・オブジェクトと対話するラッパー・クラス。 オブジェクトの型を考慮せずにデータを抽出および設定するための共通のインターフェースを提供します。

itemadapter.is_item(obj: Any)bool[ソース]

指定されたオブジェクトがサポートされている型の1つに属している場合はTrueを返し、そうでない場合はFalseを返します。

ItemAdapter.is_item のエイリアス