Scrapyライブラリ

スクレイピング

Scrapyライブラリ 詳細

Scrapyは、Pythonで書かれたウェブクローリングおよびウェブスクレイピングのための高速で強力なフレームワークです。単なるHTMLパーサーやリクエストライブラリとは異なり、リクエストのスケジューリング、レスポンスの処理、アイテムのパイプライン処理、ロギング、エラーハンドリングなど、ウェブスクレイピングプロジェクト全体を構造化し、効率的に実行するための包括的な機能を提供します。

小規模なスクレイピングから、数百万ページに及ぶ大規模なクローリングまで対応できるように設計されており、非常に堅牢で拡張性が高いのが特徴です。

1. Scrapyのインストール

ScrapyはPythonの標準ライブラリではないため、pipを使用してインストールする必要があります。

Bash

pip install scrapy

Scrapyは依存関係としてTwistedという非同期ネットワークフレームワークを使用しているため、Windows環境ではビルドツールが必要になる場合があります。その場合は、pip install wheelの後にpip install Twistedを試すか、非公式のバイナリパッケージを利用することもあります。

2. Scrapyのアーキテクチャと主要コンポーネント

Scrapyはモジュール化されたアーキテクチャを採用しており、各コンポーネントが特定の役割を担っています。

  1. Engine (エンジン): Scrapyの心臓部。すべてのコンポーネント間のデータフローを制御し、イベント駆動で処理を進めます。
  2. Scheduler (スケジューラー): エンジンから受け取ったリクエストをキューに入れて管理し、次にクロールするURLを決定します。重複リクエストのフィルタリングも行います。
  3. Downloader (ダウンローダー): スケジューラーからリクエストを受け取り、Webサイトから実際のコンテンツ(HTMLなど)をダウンロードします。
  4. Spiders (スパイダー): ユーザーが記述するクラス。Webサイトの巡回ロジックと、ダウンロードされたHTMLからデータを抽出するロジックを定義します。
  5. Item Pipelines (アイテムパイプライン): スパイダーが抽出したアイテム(データ)を処理するためのコンポーネント。データのバリデーション、クレンジング、データベースへの保存などを行います。
  6. Middlewares (ミドルウェア):
    • Downloader Middleware (ダウンローダーミドルウェア): リクエストがダウンローダーに到達する前、またはレスポンスがダウンローダーから返される途中に、リクエストやレスポンスをフックして処理を変更します(例: User-Agentの変更、プロキシの追加、リトライ処理)。
    • Spider Middleware (スパイダーミドルウェア): 入力されるレスポンスをスパイダーに送る前、またはスパイダーから出力されるアイテムやリクエストをエンジンに送る途中に、処理をフックします。

これらのコンポーネントが非同期に連携することで、高いパフォーマンスと効率的なスクレイピングを実現します。

3. Scrapyプロジェクトの作成とスパイダーの定義

3-1. プロジェクトの作成

まず、コマンドラインで新しいScrapyプロジェクトを作成します。

Bash

scrapy startproject myproject

これにより、以下のようなディレクトリ構造が生成されます。

myproject/
├── scrapy.cfg          # プロジェクト設定ファイル
└── myproject/
    ├── __init__.py
    ├── items.py        # 抽出するデータの構造定義
    ├── middlewares.py  # ミドルウェア定義
    ├── pipelines.py    # アイテムパイプライン定義
    ├── settings.py     # プロジェクト設定
    └── spiders/        # スパイダーを格納するディレクトリ
        └── __init__.py

3-2. Item(アイテム)の定義

items.pyファイルで、抽出したいデータの構造を定義します。これはPythonのdictに似ていますが、より構造化されています。

Python

# myproject/items.py
import scrapy

class MyprojectItem(scrapy.Item):
    # 抽出したいデータのフィールドを定義
    title = scrapy.Field()
    author = scrapy.Field()
    url = scrapy.Field()

3-3. Spider(スパイダー)の定義

spidersディレクトリ内に新しいPythonファイルを作成し、スパイダーを定義します。スパイダーはscrapy.Spiderを継承します。

Python

# myproject/spiders/myspider.py
import scrapy
from myproject.items import MyprojectItem # 定義したItemをインポート

class MySpider(scrapy.Spider):
    name = 'my_spider'  # スパイダーの名前(プロジェクト内で一意)
    start_urls = ['http://quotes.toscrape.com/'] # クロールを開始するURLのリスト

    # クロールしたページからデータを抽出するメソッド
    def parse(self, response):
        # CSSセレクタまたはXPathを使用してデータを抽出
        quotes = response.css('div.quote') # クラスが'quote'のdiv要素をすべて取得

        for quote in quotes:
            item = MyprojectItem()
            item['title'] = quote.css('span.text::text').get() # テキストを取得
            item['author'] = quote.css('small.author::text').get()
            item['url'] = response.url # ページのURLをそのまま取得
            yield item # 抽出したアイテムをパイプラインに送る

        # 次のページへのリンクをたどる (ページネーション)
        next_page = response.css('li.next a::attr(href)').get() # 次のページのhref属性値を取得
        if next_page is not None:
            # yield response.follow(next_page, callback=self.parse)
            # ↑ 上記は相対URLでも動作する便利なメソッド
            yield response.follow(next_page, callback=self.parse) # 次のページをクロールし、同じparseメソッドで処理

3-4. スパイダーの実行

プロジェクトのルートディレクトリで以下のコマンドを実行します。

Bash

scrapy crawl my_spider -o output.json # -o でJSONファイルに保存

my_spiderはスパイダーのname属性で指定した名前です。

4. データの抽出(セレクタ)

Scrapyは、HTML/XMLからデータを抽出するためにCSSセレクタXPathという2つの強力なセレクタメカニズムを提供します。

  • CSSセレクタ (response.css()): CSSで要素を指定するのと同じ記法。シンプルで直感的。
    • div.quote: class="quote"div 要素
    • #main-content: id="main-content" の要素
    • a::attr(href): a タグの href 属性値
    • span.text::text: class="text"span タグのテキストノード
  • XPath (response.xpath()): XMLドキュメント内のノードを選択するための言語。CSSセレクタよりも強力で、より複雑な階層関係や属性に基づいた選択が可能。
    • //div[@class="quote"]: class="quote" のすべての div 要素
    • //a/@href: すべての a タグの href 属性値
    • //span[@class="text"]/text(): class="text"span タグのテキストノード

いずれのメソッドも、Selectorオブジェクトのリストを返します。

  • selector.get(): 最初のセレクタにマッチする単一の結果(文字列)を取得。
  • selector.getall(): すべてのセレクタにマッチする結果をリスト(文字列)で取得。

5. Item Pipelines (アイテムパイプライン)

スパイダーがyield itemで返したデータは、アイテムパイプラインを通過します。 pipelines.pyファイルでクラスを定義し、process_itemメソッドを実装します。

Python

# myproject/pipelines.py
class MyprojectPipeline:
    def process_item(self, item, spider):
        # 例: タイトルフィールドの空白を削除
        if item.get('title'):
            item['title'] = item['title'].strip()

        # 例: データベースに保存
        # self.db_connection.execute("INSERT ...", item)

        return item # 次のパイプライン、または最終的な出力にアイテムを渡す

パイプラインを有効にするには、settings.pyITEM_PIPELINESに登録します。

Python

# myproject/settings.py
ITEM_PIPELINES = {
    'myproject.pipelines.MyprojectPipeline': 300, # 数値は実行順序(小さいほど早く実行)
}

6. Settings (設定)

settings.pyファイルは、Scrapyプロジェクト全体の挙動を制御します。主要な設定項目をいくつか挙げます。

  • USER_AGENT: デフォルトのUser-Agent。スクレイピング時にWebサイトのサーバーに人間からのアクセスであることを示すために設定します(例: 'Mozilla/5.0 ... Chrome/...')。
  • ROBOTSTXT_OBEY: robots.txtのルールに従うかどうか。デフォルトはTrue(推奨)。
  • CONCURRENT_REQUESTS: 最大同時リクエスト数。サーバーへの負荷を考慮して調整します。
  • DOWNLOAD_DELAY: リクエスト間の最小遅延時間(秒)。スクレイピング対象のサーバーへの負荷軽減と、IPブロック回避のために重要です。
  • COOKIES_ENABLED: クッキーを有効にするかどうか。ログインが必要なサイトではTrue
  • ITEM_PIPELINES: 使用するアイテムパイプラインとその実行順序。
  • DOWNLOADER_MIDDLEWARES: 使用するダウンローダーミドルウェアとその実行順序。
  • AUTOTHROTTLE_ENABLED: 自動スロットリング(サーバーへの負荷に応じてリクエスト速度を自動調整)を有効にするかどうか。推奨設定。

7. Middlewares (ミドルウェア)

ミドルウェアは、Scrapyの強力な拡張ポイントです。リクエスト/レスポンス処理の途中でカスタムロジックを挿入できます。

  • Downloader Middleware:
    • User-Agentのランダム変更
    • プロキシの自動切り替え
    • HTTPリダイレクトのハンドリング
    • リクエストのリトライ処理
    • クローラーのIPアドレスを変更するためのTor統合
  • Spider Middleware:
    • スパイダーに渡すレスポンスを前処理
    • スパイダーからエンジンに送るリクエストやアイテムを後処理

ミドルウェアもsettings.pyで有効化・設定します。

Python

# myproject/settings.py
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.MyprojectDownloaderMiddleware': 543,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, # デフォルトを無効化
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None, # デフォルトを無効化
    'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400, # 外部ライブラリ
}

8. Scrapy Shell

Scrapyには、対話的なシェル (scrapy shell) が付属しており、スパイダーを開発する際に非常に便利です。特定のURLにアクセスし、そのレスポンスに対してCSSセレクタやXPathを試しながらデータを抽出するコードをテストできます。

Bash

scrapy shell "http://quotes.toscrape.com/page/1/"

シェル内では、response オブジェクトが自動的に利用可能です。

Python

# シェル内で
response.css('title::text').get()
response.xpath('//h1/a/text()').get()

9. まとめ

Scrapyは、ウェブスクレイピングとクローリングのための単なるライブラリではなく、完全なフレームワークです。

  • 非同期処理: 高速な並行処理を実現。
  • 構造化: アイテム、パイプライン、ミドルウェアによりプロジェクトを整理。
  • 拡張性: 各コンポーネントを自由にカスタマイズ・拡張可能。
  • 堅牢性: リクエストスケジューリング、リトライ、重複フィルタリングなどにより安定した動作。
  • 開発者ツール: Scrapy Shellなど、開発を助けるツールが豊富。

小規模なデータ収集から大規模なWebコンテンツ分析まで、Pythonでウェブデータを扱うプロフェッショナルなタスクにおいて、Scrapyは強力な選択肢となります。ただし、その多機能さゆえに学習曲線はRequestsBeautifulSoup単体よりも急ですが、一度習得すれば、複雑なスクレイピングプロジェクトを効率的に構築・運用できるようになります。

ウェブスクレイピングを行う際は、必ず対象サイトの利用規約を確認し、robots.txtに従い、サーバーに過度な負荷をかけないよう倫理的な行動を心がけてください。