Scrapyは、Pythonで使える強力なウェブスクレイピングフレームワークであり、大規模なデータ収集に特化しています。大規模なクローラーを構築するには、効率的なデータ収集、リトライ、並列処理、データ保存の仕組みが不可欠です。この記事では、Scrapyを使って大規模なウェブクローラーを構築する方法をステップごとに紹介します。

Scrapyとは?

Scrapyは、Pythonベースのウェブスクレイピングフレームワークで、ウェブサイトから効率的にデータを収集するためのツールです。Scrapyの最大の特徴は、並列処理とスケジューリングのサポートによる高速なクローリングです。さらに、スクレイピングしたデータをJSON、CSV、データベースなどに簡単に保存でき、エラー処理や再試行も自動化されています。

Scrapyの主な機能

  • 並列処理による高速クローリング
  • リトライ機能でエラーや失敗時に再試行
  • シンプルな設定で、データパイプラインやミドルウェアをカスタマイズ可能
  • JSONやCSV、データベースへの簡単なデータ保存
  • ロボット排除規約(robots.txt)に準拠したクローリング

Scrapyのインストール

Scrapyをインストールするには、次のコマンドをターミナルで実行します。

pip install scrapy

インストールが完了したら、Scrapyプロジェクトを作成します。

プロジェクトの作成

次に、Scrapyプロジェクトを作成します。

scrapy startproject mycrawler

これで、新しいScrapyプロジェクトがmycrawlerというディレクトリに作成されます。次に、クローラー(スパイダー)の実装に進みます。

Scrapyスパイダーの作成

スパイダーは、Scrapyでウェブページをクロールし、データを収集するためのメインコンポーネントです。まず、spidersフォルダに新しいスパイダーを作成します。

スパイダーの基本的な実装

以下は、例としてあるニュースサイトをクロールしてタイトルとリンクを取得するスパイダーのコードです。

import scrapy
class NewsSpider(scrapy.Spider):
    name = 'news_spider'
    start_urls = ['https://example-news-site.com/']
    def parse(self, response):
        # 各ニュース記事のタイトルとリンクを抽出
        for article in response.css('div.article'):
            yield {
                'title': article.css('h2.title::text').get(),
                'link': article.css('a::attr(href)').get(),
            }
        # 次のページがあれば、再度リクエストを送信
        next_page = response.css('a.next-page::attr(href)').get()
        if next_page:
            yield response.follow(next_page, self.parse)

コードの説明

  • start_urls:スパイダーが最初にアクセスするURLのリスト。
  • parse():レスポンスを解析し、データを抽出するメソッド。ここでは、記事のタイトルとリンクを取得しています。
  • yield:収集したデータや次のリクエストを返す際に使用します。yieldは並列処理をサポートしているため、効率的なクローリングが可能です。 このスパイダーは、ページ内の記事をスクレイピングし、次のページがあれば再帰的にクローリングを続けます。

並列処理の設定

Scrapyはデフォルトで並列処理をサポートしていますが、クローリングの効率をさらに高めるためには、いくつかの設定を調整することが重要です。settings.pyファイルで、並列リクエスト数やダウンロード遅延などの設定を行います。

並列処理の設定例

# settings.py内
# 並列リクエスト数の設定(デフォルト: 16)
CONCURRENT_REQUESTS = 32
# 同一ドメインへの最大リクエスト数
CONCURRENT_REQUESTS_PER_DOMAIN = 16
# ダウンロードの遅延(0.5秒)
DOWNLOAD_DELAY = 0.5
# リトライ回数
RETRY_TIMES = 3
# クローリング速度を適度に抑える設定
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1  # 初期遅延時間
AUTOTHROTTLE_MAX_DELAY = 60   # 最大遅延時間
  • CONCURRENT_REQUESTS:同時に処理するリクエストの数。これを増やすとクローリング速度が向上します。
  • DOWNLOAD_DELAY:サーバー負荷を軽減するためのリクエスト間の遅延時間。適度な値を設定することで、サイトに負担をかけずに効率的にクローリングできます。
  • AUTOTHROTTLE_ENABLED:自動でクローリング速度を調整する機能。サーバーの負荷に応じて速度を調整します。

エラー処理とリトライ

大規模なクローリングでは、リクエストの失敗やサーバーエラーが発生する可能性があります。Scrapyはこれに対処するため、リトライ機能が標準で提供されています。RETRY_TIMESRETRY_HTTP_CODESの設定で、エラー発生時のリトライ回数や対象のHTTPステータスコードを指定できます。

リトライ設定の例

# リトライ回数の設定
RETRY_TIMES = 5
# リトライ対象となるHTTPステータスコード
RETRY_HTTP_CODES = [500, 502, 503, 504, 408]

これにより、サーバーの一時的な障害に対してリクエストを再送し、クローリングが途切れないようにできます。

データの保存

Scrapyは、クローリングしたデータをさまざまなフォーマットで保存できます。JSON、CSV、XMLなどの形式に簡単にエクスポート可能です。

JSON形式でデータを保存する例

scrapy crawl news_spider -o output.json

このコマンド を実行すると、収集したデータがoutput.jsonに保存されます。データフォーマットはCSVやXMLにも変更可能です。

データパイプラインを使ったデータベース保存

大規模なクローリングでは、収集データをデータベースに直接保存することが一般的です。pipelines.pyにデータパイプラインを実装し、データをMySQLやMongoDBなどのデータベースに保存します。

import pymongo
class MongoPipeline:
    def open_spider(self, spider):
        # MongoDBに接続
        self.client = pymongo.MongoClient('localhost', 27017)
        self.db = self.client['scrapy_db']
    def close_spider(self, spider):
        self.client.close()
    def process_item(self, item, spider):
        # データをMongoDBに保存
        self.db['news'].insert_one(dict(item))
        return item

settings.pyでこのパイプラインを有効化します。

ITEM_PIPELINES = {
   'mycrawler.pipelines.MongoPipeline': 300,
}

スケジューリングと自動化

Scrapyは定期的に実行するためのスケジューリング機能をサポートしていませんが、LinuxのcronやWindowsのタスクスケジューラを使って定期的にクローリングを自動化することが可能です。

Linuxでのcronによるスケジューリング

以下のようにcronを設定して、1時間ごとにクローラーを実行できます。

0 * * * * cd /path/to/project && scrapy crawl news_spider

まとめ

Scrapyは、大規模なウェブクローラーを構築するための強力なフレームワークです。並列処理やリトライ機能を標準でサポートしているため、効率的なクローリングが可能です。また、データの保存やスケジューリングも簡単に行えるため、大量のデータを自動で収集・保存できるシステムを構築できます。 今回紹介した設定や機能を活用し、自分のプロジェクトに最適な大規模クローラーを構築してみてください。