修行の場

知人が言っていました。これは修行だと。

Scrapyの設計のPipeline周りの設計について

背景

ItemPipelineのドキュメントとクラス構造をさらっと見たのでまとめます。

ItemPipeline

ItemPipelineの基本

ItemPipelineのDocによると、ImagePipelineはitemに対して、map, filter, 永続化のような処理をするのにscrapy中でよく使われるクラスです。

下記のメソッドの使用例が挙げられています。

  • from_crawler: 設定へのアクセス
  • open_spider: ファイルやMongoDBセッションの初期化
  • close_spider: ファイルやMongoDBセッションのクローズ

また、アイテムに対する処理をするprocess_itemでは、関数の終了の仕方によってscrapy側の処理が変わります。DropItem例外によりFW側にアイテムの無視を伝えたり、Deferedクラスを返すことで、FWに非同期処理を実行させたりできるようになっています。

Scrapyで用意されているItem Pipeline

Scrapy側で用意されているItemPipelineがあります。

ドキュメントによると、ユーザが使いそうなのはImagesPipelineとFilesPipelineとのことで、それぞれ

  • FilesPipeline: 最近ダウンロードしたファイルの再ダウンロードの防止、URIによるダウンロード先の切り替え。内部的なキューにより、同じファイルのダウンロード防止
  • ImgaesPipeline: 画像の形式の統一(RGB,jpeg)。サムネイルの生成、制約

FilesPipeline, ImagesPipeline利用時のポイントは、

  1. HTMLページなどと共通のスケジューラを使っているがファイルのほうが優先度が高くなっている。
  2. ダウンロードのステータスとファイル情報はタプルのリストとして提供される。(ドキュメントが間違っている?)

自前のアイテムクラスを定義したり、どのフィールドにURLを格納するか設定する方法もある。ImagesPipelineクラスを継承した場合、フィールドを設定を変更する方法がFW側で提供されている。

なにか拡張したItemPipelineを作りたい場合、item_completedとget_media_requestsメソッドをオーバーライドするのが最低限。

もう少し内部の話

ItemPipelineのクラスの設計は画像の通りです。

ファイルの保存先をFilesStoreクラスとして抽象化することf:id:sam_yusuke:20190227211239p:plainで、FilesPipelineおよび子クラスはAWS、GCS、ローカルと意識せずファイル操作のコードになるようになっています。 FilesPilelineのコンストラクタ引数に指定する、ファイルの保存先をファイルパスではなく、URIとして与えるようなっていることで、FilesPipeline中で自動的に対応するFilesStoreクラスを作るようになっています。

URIの表現能力は、階層構造の中の1ノードを示すのによいため、ローカルorリモートのファイルシステムのみ扱う今回のケースでは十分カバーされている気がします。

MediaPipelineの責務は不明だが、FilesPipeline, ImagesPipelineはドキュメントでなんとなくわかる。 MediaPipelineを無視すれば、ImagesPipeline is a FilesPipelineなので関係としてはわかりやすいし、設計として変ではない。

拡張性としても、格納先はファイルシステムに似た構造であれば、新しいFileStoreクラスを作れば簡単に拡張できるし、ダウンロード対象もVideosや音楽用にMusicsPipelineやVideosPipelineもFilesPipelineクラスを継承して作れる。 自前のデータ構造用のPipelineも作れる。

気になったところ

  • Python2.7対応のためにabsクラスが使えず、代わりにドキュメントでインターフェースの説明をしている。可能な限りPython2.7は切り捨てたい。コードコメントに書いたほうが探しやすい。しかし、すべてのライブラリにそれを期待しては行けない。
  • 戻り値、例外によりFW側に何かをお願いする仕組みは良い。処理なし、非同期処理、例外の3つだけで単純なのでわかりやすい。FW側へのお願いの数が多いと厳しいかもしれません。
  • Scrapyはソースコードより、ドキュメントに一通り目を通しておくと、正しく効率的な使い方がわかりそう。
  • Pipelineはページのクローリングと収集対象データの疎結合化に寄与していて、一度収集ロジックを作ると、様々なWebサイト用の同じ種類のデータを収集できるようになる。
  • これはETL(Extraction, transform, load)ではなく、ETP(Extraction/transform/Persistent)だが、LでもPでも似たようなものなのでほぼ同じ仕組みがETLでも使えるのでは?