Item¶
スクレイピングの主な目的は、構造化されていないソース(通常はWebページ)から構造化されたデータを抽出することです。ScrapyのSpiderは、抽出されたデータをPythonのdictとして返すことができます。便利で手軽なのですが、Pythonのdictでは構造化が不十分です。特に、多くのSpiderを持つ大規模なプロジェクトでは、簡単にフィールド名をtypoしたり、矛盾したデータを返してしまいます。
共通の出力データ形式を定義するために、Scrapyは Item
クラスを提供します。 Item
オブジェクトは、抽出されたデータを収集するためのシンプルなコンテナです。利用可能なフィールドを宣言するための便利な構文を備えた dictライク なAPIを提供します。
さまざまなScrapyコンポーネントがItemが提供する情報を使用します。たとえば、エクスポーターは宣言されたフィールドを参照してエクスポートする列を調べ、シリアライズはItemフィールドのメタデータを利用してカスタマイズできます。 trackref
はメモリのリークを見つけるための項目インスタンスを追跡します( Debugging memory leaks with trackref 参照)。
Itemの宣言¶
Itemはシンプルなクラス定義と Field
オブジェクトを使用して宣言されます。次に例を示します。
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
注釈
Django に精通している人は、Itemが Django Models と同じように宣言されていることに気づくでしょう。ただScrapyのItemsは異なるフィールド型の概念がないので、それよりはるかに簡単です。
Itemフィールド¶
Field
オブジェクトは、各フィールドのメタデータを指定するために使用されます。たとえば、上記の例の last_updated
フィールドのserializer関数がそれにあたります。
各フィールドには任意の種類のメタデータを指定できます。 Field
オブジェクトが受け入れる値に制限はありません。同じ理由から、使用可能なすべてのメタデータキーの参照リストはありません。 Field
オブジェクトで定義された各キーは、別のコンポーネントによって使用され、それらのコンポーネントだけがそれを知ることができます。また、自分のニーズに合わせて、プロジェクト内の他の Field
キーを定義して使用することもできます。 Field
オブジェクトの主な目的は、すべてのフィールドのメタデータを一ヶ所で定義する方法を提供することです。通常、その動作が各フィールドに依存するコンポーネントは、特定のフィールドキーを使用してその動作を構成します。各コンポーネントでどのメタデータキーが使用されているかについては、ドキュメントを参照する必要があります。
項目を宣言するために使用される Field
オブジェクトは、クラスの属性として割り当てられたままではないことに注意してください。代わりに、 Item.fields
属性を使用してアクセスできます。
Itemの操作¶
上記で宣言された Product
Itemを使用したタスクの例をいくつか示します。APIは dict API と非常によく似ています。
Itemの作成¶
>>> 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')]
その他の一般的なタスク¶
Itemのコピー
>>> product2 = Product(product)
>>> print(product2)
Product(name='Desktop PC', price=1000)
>>> product3 = product2.copy()
>>> print(product3)
Product(name='Desktop PC', price=1000)
Itemからdictを作成する
>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}
dictからItemを作成する
>>> 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の拡張¶
元のItemのサブクラスを宣言することで、フィールドを追加したり、一部のフィールドのメタデータを変更したりなど、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
メタデータキーが追加(または置換)され、以前に存在したすべてのメタデータ値が保持されます。