ファイルと画像のダウンロードおよび処理¶
Scrapyは、特定のアイテムに添付されたファイルをダウンロードするための再利用可能な アイテム・パイプライン を提供します(たとえば、製品をスクレイピングし、画像をローカルにダウンロードする場合)。 これらのパイプラインは少しの機能と構造を共有します(メディア・パイプラインと呼びます)が、通常はファイル・パイプラインまたは画像パイプラインを使用します。
両方のパイプラインは以下の機能を実装しています:
最近ダウンロードした媒体の再ダウンロードを避ける
メディアの保存場所の指定(ファイル・システム・ディレクトリ、FTPサーバ、Amazon S3バケット、Google Cloud Storageバケット)
画像パイプラインには、画像を処理するためのいくつかの追加機能があります:
ダウンロードしたすべての画像を共通の形式(JPG)とモード(RGB)に変換する
サムネイル生成
画像の幅/高さをチェックして、最小の制約を満たしていることを確認します
パイプラインは、現在ダウンロードがスケジュールされている媒体URLの内部キューを保持し、到着したレスポンスのうち、同じ媒体を含むレスポンスをそのキューに接続します。これにより、複数のアイテムで共有されている場合に同じ媒体を複数回ダウンロードすることがなくなります。
ファイル・パイプラインの使用¶
FilesPipeline
を使用する場合の典型的なワークフローは次のようになります:
Spiderでは、アイテムをスクレイプし、目的のURLを
file_urls
フィールドに入れます。アイテムはスパイダーから返され、アイテム・パイプラインに送られます。
アイテムが
FilesPipeline
に到達すると、file_urls
フィールドのURLは標準のScrapyスケジューラーとダウンローダー(スケジューラーとダウンローダーのミドルウェアが再利用されることを意味します)を使用してダウンロード用にスケジュールされます。しかし、他のページの処理より高い優先度で、他のページがスクレイピングされる前にダウンロード用の処理を行います。ファイルのダウンロードが完了する(または何らかの理由で失敗する))まで、アイテムはその特定のパイプライン・ステージでロックされたままになります。ファイルがダウンロードされると、別のフィールド(
files
)に結果が入力されます。このフィールドには、ダウンロードしたパスと、スクレイプされた元のURL(file_urls
フィールドから取得)と、ファイルのチェックサムと、ファイルのステータス、のような、ダウンロードしたファイルに関する情報を含む辞書(dict)のリストが含まれます。files
フィールドのリストにあるファイルは、 元のfile_urls
フィールドと同じ順序を保持します。ファイルのダウンロードに失敗した場合、エラーがログに記録され、ファイルはfiles
フィールドに存在しません。
画像パイプラインの使用¶
ImagesPipeline
を使用することは、使用されるデフォルトのフィールド名が異なることを除き FilesPipeline
を使用することとよく似ています。あなたがアイテムの画像URLに image_urls
を使用すると、ダウンロードした画像に関する情報が images
フィールドに入力されます。
画像ファイルに ImagesPipeline
を使用する利点は、サムネイルの生成やサイズに基づいた画像のフィルタリングなどの追加機能を設定できることです。
画像パイプラインには Pillow 4.0.0 以降が必要です。 これは、画像のサムネイル化と JPEG/RGB 形式への正規化に使用されます。
あなたのメディア・パイプラインを有効にする¶
メディア・パイプラインを有効にするには、まずプロジェクトに ITEM_PIPELINES
設定を追加する必要があります。
画像パイプラインを使うには:
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}
ファイル・パイプラインを使うには:
ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}
注釈
ファイルと画像のパイプラインの両方を同時に使用することもできます。
次に、ダウンロードした画像の保存に使用される有効な値でターゲット・ストレージ設定を構成(configure)します。 そうしないと、パイプラインは ITEM_PIPELINES
設定に含めても無効のままになります。
ファイル・パイプラインの場合、 FILES_STORE
設定を設定します:
FILES_STORE = '/path/to/valid/dir'
画像パイプラインの場合、 IMAGES_STORE
設定を設定します:
IMAGES_STORE = '/path/to/valid/dir'
サポートされるストレージ¶
ファイル・システム・ストレージ¶
ファイルは、ファイル名のURLから生成する SHA1 hash を使用して保存されます。
たとえば、次の画像URL:
http://www.example.com/image.jpg
SHA1 hash
は:
3afec3b4765f8f0a07b78f98c07b83f013567a0a
ダウンロードされ、以下のファイルに保存されます:
<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg
ここで:
<IMAGES_STORE>
は、画像パイプラインのIMAGES_STORE
設定で定義されているディレクトリです。`` full`` は、(使用する場合)サムネイルから完全な画像を分離するためのサブディレクトリです。詳細は 画像のサムネイル生成 を参照。
FTPサーバ・ストレージ¶
バージョン 2.0 で追加.
FILES_STORE
と IMAGES_STORE
はFTPサーバを指し示すことができます。Scrapyはファイルをサーバーに自動的にアップロードします。
FILES_STORE
と IMAGES_STORE
以下のいずれかの形式で記述しなければなりません:
ftp://username:password@address:port/path
ftp://address:port/path
username
や password
が指定されていない場合、それぞれ FTP_USER
設定や FTP_PASSWORD
設定から取得されます。
FTPは、アクティブまたはパッシブの2つの異なる接続モードをサポートします。 Scrapyはデフォルトでパッシブ接続モードを使用します。 代わりにアクティブ接続モードを使用するには、 FEED_STORAGE_FTP_ACTIVE
設定を True
に設定します。
Amazon S3 ストレージ¶
botocore 1.4.87 以上がインストールされていれば、FILES_STORE
と IMAGES_STORE
はAmazon S3バケットを表すことができます。 Scrapyは自動的にファイルをバケットにアップロードします。
たとえば、以下は有効な IMAGES_STORE
値です:
IMAGES_STORE = 's3://bucket/images'
あなたは FILES_STORE_S3_ACL
と IMAGES_STORE_S3_ACL
設定によって定義される、保存されたファイルに使用されるアクセス制御リスト(ACL)ポリシーを変更できます。デフォルトでは、ACLは private
に設定されています。ファイルを公開するには、 public-read
ポリシーを使用します:
IMAGES_STORE_S3_ACL = 'public-read'
詳細については、Amazon S3開発者ガイドの canned ACLs を参照してください。
あなたは他のS3のようなストレージも使用できます。自己ホスト型の Minio や s3.scality のようなストレージです。あなたがする必要があるのはScrapy設定でendpointオプションを設定することです:
AWS_ENDPOINT_URL = 'http://minio.example.com:9000'
セルフホスティングの場合は、SSLを使用する必要はなく、SSL接続を確認する必要もないと感じるかもしれません:
AWS_USE_SSL = False # or True (None by default)
AWS_VERIFY = False # or True (None by default)
Google Cloud ストレージ¶
FILES_STORE
と IMAGES_STORE
は、Google Cloud Storageバケットを表すことができます。Scrapyは自動的にファイルをバケットにアップロードします( google-cloud-storage が必要です)。
たとえば、以下は有効な IMAGES_STORE
および GCS_PROJECT_ID
設定です:
IMAGES_STORE = 'gs://bucket/images/'
GCS_PROJECT_ID = 'project_id'
認証については、この documentation を参照してください。
FILES_STORE_GCS_ACL
および IMAGES_STORE_GCS_ACL
設定によって定義される、保存されたファイルに使用されるアクセス制御リスト(ACL)ポリシーを変更できます。デフォルトでは、ACLは ''
(空の文字列)に設定されます。これは、Cloud Storageがバケットのデフォルト・オブジェクトACLをオブジェクトに適用することを意味します。ファイルを公開するには、 publicRead
ポリシーを使用します:
IMAGES_STORE_GCS_ACL = 'publicRead'
詳細については、Google Cloud Platform Developer Guide の Predefined ACLs を参照してください。
使用例¶
メディア・パイプラインを使用するには、まず、 メディア・パイプラインを有効にする を行います。
次に、スパイダーがURLフィールド(file_urls
(ファイル・パイプラインの場合) または image_urls
(画像パイプラインの場合)) を含む アイテム・オブジェクト を返すならば、そのパイプラインは結果をそれぞれのフィールド (files
または images
) の下に配置します。
フィールドが事前に定義されている アイテム型 を使用する場合は、URLフィールドと結果フィールドの両方を定義する必要があります。 たとえば、画像パイプラインを使用する場合、アイテムは image_urls
フィールドと images
フィールドの両方を定義しなければなりません。 たとえば、 Item
クラスを使用します:
import scrapy
class MyItem(scrapy.Item):
# ... other item fields ...
image_urls = scrapy.Field()
images = scrapy.Field()
URLキーまたは結果キーに別のフィールド名を使用する場合は、それをオーバーライドすることもできます。
ファイル・パイプラインでは、 FILES_URLS_FIELD
and/or FILES_RESULT_FIELD
設定を設定します:
FILES_URLS_FIELD = 'field_name_for_your_files_urls'
FILES_RESULT_FIELD = 'field_name_for_your_processed_files'
画像パイプラインでは、 IMAGES_URLS_FIELD
and/or IMAGES_RESULT_FIELD
設定を設定します:
IMAGES_URLS_FIELD = 'field_name_for_your_images_urls'
IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'
より複雑なものが必要で、カスタム・パイプラインの動作をオーバーライドする場合は、 メディア・パイプラインの拡張 を参照してください。
ImagePipelineを継承する複数の画像パイプラインがあり、あなたが異なるパイプラインで異なる設定を行いたい場合は、パイプライン・クラス名を大文字化した名前を先頭に付けた設定キーを設定できます。例えば、パイプラインの名前がMyPipelineで、カスタムIMAGES_URLS_FIELDが必要な場合は、設定MYPIPELINE_IMAGES_URLS_FIELDを定義すると、カスタム設定が使用されます。
追加機能¶
ファイルの有効期限¶
画像パイプラインは、最近ダウンロードされたファイルのダウンロードを回避します。この保持遅延を調整するには、 FILES_EXPIRES
(または、画像パイプラインの場合は IMAGES_EXPIRES
)設定を使用します。これは、日数で保持遅延を指定します:
# 120 days of delay for files expiration
FILES_EXPIRES = 120
# 30 days of delay for images expiration
IMAGES_EXPIRES = 30
両方の設定のデフォルト値は90日です。
FilesPipelineをサブクラス化するパイプラインがあり、それに対して別の設定が必要な場合は、クラス名を大文字化した名前を先頭に付けた設定キーを設定できます。例えば、パイプラインの名前がMyPipelineで、カスタムFIlE_EXPIERSが必要な場合は、以下のように設定すると、カスタム設定が使用されます。
MYPIPELINE_FILES_EXPIRES = 180
こうすると、パイプライン・クラスMyPipelineの有効期限は180に設定されます。
画像のサムネイル生成¶
画像Pipelineは、ダウンロードした画像のサムネイルを自動的に作成できます。
この機能を使用するには、 IMAGES_THUMBS
を辞書に設定する必要があります。ここで、キーはサムネイル名であり、値はその寸法です。
例えば:
IMAGES_THUMBS = {
'small': (50, 50),
'big': (270, 270),
}
この機能を使用すると、画像パイプラインは指定された各サイズのサムネイルをこの形式で作成します:
<IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg
ここで:
<size_name>
はIMAGES_THUMBS
辞書キーで指定されたもの(small
、big
など)です<image_id>
は画像のURLの SHA1 hash です
small
および big
のサムネイル名を使用して保存された画像ファイルの例:
<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
最初のものは、サイトからダウンロードされたフル画像です。
小さな画像を除外する¶
画像パイプラインを使用する場合、 IMAGES_MIN_HEIGHT
および IMAGES_MIN_WIDTH
設定で最小許容サイズを指定することにより、小さすぎる画像をドロップできます。
例えば:
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110
注釈
サイズの制約は、サムネイルの生成にはまったく影響しません。
1つのサイズ制約または両方を設定することができます。両方を設定すると、両方の最小サイズを満たす画像のみが保存されます。上記の例では、サイズが(105×105)または(105×200)または(200×105)の画像はすべて削除されます。これは、少なくとも1つの寸法が制約よりも短いためです。
デフォルトでは、サイズの制限はないため、すべての画像が処理されます。
リダイレクトを許可する¶
デフォルトでは、メディア・パイプラインはリダイレクトを無視します。つまり、メディア・ファイルURLリクエストへのHTTPリダイレクトは、メディアのダウンロードが失敗したと見なされることを意味します。
メディアのリダイレクトを処理するには、この設定を True
に設定します:
MEDIA_ALLOW_REDIRECTS = True
メディア・パイプラインの拡張¶
ここで、カスタム・ファイル・パイプラインでオーバーライドできるメソッドを参照してください:
- class scrapy.pipelines.files.FilesPipeline[ソース]¶
- file_path(self, request, response=None, info=None, *, item=None)[ソース]¶
このメソッドは、ダウンロードされたアイテムごとに1回呼び出されます。 指定された
response
から始まるファイルのダウンロード・パスを返します。response
に加えて、このメソッドは元のrequest
とinfo
とitem
を受け取ります。このメソッドをオーバーライドして、各ファイルのダウンロード・パスをカスタマイズできます。
たとえば、ファイルのURLが通常のパスのように終わる場合(例
https://example.com/a/b/c/foo.png
)、次のアプローチを使用して、全てのファイルを元のファイル(例files/foo.png
)でfiles
フォルダーにダウンロードできます:import os from urllib.parse import urlparse from scrapy.pipelines.files import FilesPipeline class MyFilesPipeline(FilesPipeline): def file_path(self, request, response=None, info=None, *, item=None): return 'files/' + os.path.basename(urlparse(request.url).path)
同様に、
item
を使用して、アイテムのプロパティに基づいてファイル・パスを決定できます。デフォルトでは
file_path()
メソッドはfull/<request URL hash>.<extension>
を返します。バージョン 2.4 で追加: アイテム パラメータ。
- get_media_requests(item, info)[ソース]¶
ワークフローにあるように、パイプラインはアイテムからダウンロードする画像のURLを取得します。これを行うには、
get_media_requests()
メソッドをオーバーライドして、各ファイルURLのリクエストを返すことができます:from itemadapter import ItemAdapter def get_media_requests(self, item, info): adapter = ItemAdapter(item) for file_url in adapter['file_urls']: yield scrapy.Request(file_url)
これらのリクエストはパイプラインによって処理され、ダウンロードが完了すると、結果が
item_completed()
メソッドに2要素タプルのリストとして送信されます。各タプルには(success, file_info_or_error)
が含まれます:success
は、画像が正常にダウンロードされた場合はTrue
であるブール値であり、何らかの理由で失敗した場合はFalse
ですfile_info_or_error
は、(successTrue
の場合)以下のキーを含む辞書です。問題が発生した場合はFailure
です。url
- ファイルのダウンロード元のURL。これはget_media_requests()
メソッドから返されるリクエストのURLです。path
- ファイルが保存されたパス(FILES_STORE
への相対パス)checksum
- 画像コンテンツのMD5ハッシュ(MD5 hash)status
- ファイル・ステータスの表示。バージョン 2.2 で追加.
以下のいずれかになります:
downloaded
- ダウンロードされたファイル。uptodate
- それは最近ダウンロードされたことがあるため、ファイルの有効期限ポリシーに従って、ファイルはダウンロードされませんでした。cached
- 同一のファイルを共有している別のアイテムによって、ファイルのダウンロードがすでにスケジュールされていました。
item_completed()
が受け取るタプルのリストは、get_media_requests()
メソッドから返されたリクエストと同じ順序を保持することが保証されています。以下は
results
引数の典型的な値です:[(True, {'checksum': '2b00042f7481c7b056c4b410d28f33cf', 'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg', 'url': 'http://www.example.com/files/product1.pdf', 'status': 'downloaded'}), (False, Failure(...))]
デフォルトでは、
get_media_requests()
メソッドはNone
を返します。これは、アイテムにダウンロードするファイルがないことを意味します。
- item_completed(results, item, info)[ソース]¶
FilesPipeline.item_completed()
メソッドは、1つのアイテムに対するすべてのファイルリクエストが完了した(ダウンロードが完了したか、何らかの理由で失敗した)ときに呼び出されます。item_completed()
メソッドは、後続のアイテム・パイプライン・ステージに送信される出力を返す必要があるため、パイプラインの場合と同様に、アイテムを返す(またはドロップする)必要があります。以下は
item_completed()
メソッドの例で、(結果で渡された)ダウンロードしたファイル・パスをfile_paths
アイテム・フィールドに保存し、ファイルが含まれていない場合はアイテムをドロップします:from itemadapter import ItemAdapter from scrapy.exceptions import DropItem def item_completed(self, results, item, info): file_paths = [x['path'] for ok, x in results if ok] if not file_paths: raise DropItem("Item contains no files") adapter = ItemAdapter(item) adapter['file_paths'] = file_paths return item
デフォルトでは、
item_completed()
メソッドはアイテムを返します:
カスタム画像パイプラインでオーバーライドできるメソッドをご覧ください:
- class scrapy.pipelines.images.ImagesPipeline[ソース]¶
ImagesPipeline
はFilesPipeline
の拡張であり、フィールド名をカスタマイズして画像のカスタム動作を追加します。- file_path(self, request, response=None, info=None, *, item=None)[ソース]¶
このメソッドは、ダウンロードされたアイテムごとに1回呼び出されます。 指定された
response
から始まるファイルのダウンロード・パスを返します。response
に加えて、このメソッドは元のrequest
とinfo
とitem
を受け取ります。このメソッドをオーバーライドして、各ファイルのダウンロード・パスをカスタマイズできます。
たとえば、ファイルのURLが通常のパスのように終わる場合(例
https://example.com/a/b/c/foo.png
)、次のアプローチを使用して、全てのファイルを元のファイル(例files/foo.png
)でfiles
フォルダーにダウンロードできます:import os from urllib.parse import urlparse from scrapy.pipelines.images import ImagesPipeline class MyImagesPipeline(ImagesPipeline): def file_path(self, request, response=None, info=None, *, item=None): return 'files/' + os.path.basename(urlparse(request.url).path)
同様に、
item
を使用して、アイテムのプロパティに基づいてファイル・パスを決定できます。デフォルトでは
file_path()
メソッドはfull/<request URL hash>.<extension>
を返します。バージョン 2.4 で追加: アイテム パラメータ。
- get_media_requests(item, info)[ソース]¶
FilesPipeline.get_media_requests()
メソッドと同じように機能しますが、画像のURLに異なるフィールド名を使用します。各画像URLのリクエストを返す必要があります。
- item_completed(results, item, info)[ソース]¶
ImagesPipeline.item_completed()
メソッドは、1つのアイテムに対するすべての画像リクエストが完了した(ダウンロードが完了したか、何らかの理由で失敗した)ときに呼び出されます。FilesPipeline.item_completed()
メソッドと同じように機能しますが、画像のダウンロード結果を保存するために異なるフィールド名を使用します。デフォルトでは、
item_completed()
メソッドはアイテムを返します:
カスタム画像パイプライン例¶
上記で例示されたメソッドについて、以下に画像パイプラインの完全な例を示します:
import scrapy
from itemadapter import ItemAdapter
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
class MyImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url)
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem("Item contains no images")
adapter = ItemAdapter(item)
adapter['image_paths'] = image_paths
return item
カスタム・メディア・パイプライン・コンポーネントを有効にするには、以下例のように、クラス・インポート・パスを ITEM_PIPELINES
設定に追加する必要があります:
ITEM_PIPELINES = {
'myproject.pipelines.MyImagesPipeline': 300
}