CodeKitchen

Scrapyを利用した実践的なWebクローリングのテクニック

python scrapy

1. Scrapy の概要

Scrapy とは何か

Scrapy は、Python で書かれた強力な Web クローリングおよびスクレイピングフレームワークです。Web サイトからデータを効率的に収集し、構造化されたデータとして抽出することができます。Scrapy は、大規模なクローリングプロジェクトに適しており、柔軟性と拡張性に優れています。

Scrapy の特徴と利点

  1. 高速性: Scrapy は、非同期ネットワーク処理を利用しているため、高速にクローリングを行うことができます。複数のリクエストを並行して処理することで、効率的にデータを収集できます。

  2. 柔軟性: Scrapy は、柔軟なアーキテクチャを持っており、開発者はカスタムコンポーネントを簡単に追加・拡張できます。これにより、プロジェクトごとに異なる要件に対応することができます。

  3. 強力なセレクター: Scrapy は、XPath と CSS セレクターをサポートしており、Web ページ内の特定の要素を簡単に抽出できます。これにより、目的のデータを正確に取得することができます。

  4. リクエストの管理: Scrapy は、リクエストの管理機能を提供しており、リクエストの優先度付け、リトライ、クッキーの処理などを簡単に行うことができます。

  5. ミドルウェアのサポート: Scrapy は、リクエストとレスポンスに対して処理を行うミドルウェアをサポートしています。これにより、データの前処理、エラー処理、認証などを柔軟に実装できます。

  6. アイテムパイプライン: Scrapy は、抽出したデータを処理するためのアイテムパイプラインを提供しています。パイプラインを使用することで、データのクリーニング、検証、保存などを簡単に行うことができます。

2. Scrapy の主要コンポーネント

コンポーネントの概要と関係性

scrapy.cfgScrapy_ProjectScrapy EngineTarget WebsiteSchedulerDownloaderInternetDownloader_MiddlewaresSpider_MiddlewaresSpiderItemsItem_PipelineCleaned DataData Output

この図は、Scrapyのコンポーネントの概要と関係性を示しています。scrapy.cfgの設定ファイルからScrapyプロジェクトが作成され、Scrapy Engineが起動します。Scrapy Engineは、Scheduler、Downloader、Middlewares、Spider、Item Pipelineなどの主要なコンポーネントを制御し、データの流れを管理します。Downloaderはインターネットを介してターゲットのウェブサイトにリクエストを送信し、レスポンスを受け取ります。Spiderはレスポンスからアイテムを生成し、Item Pipelineがアイテムのクリーニングと検証を行います。最終的に、クリーンアップされたデータが出力されます。

Scrapyのデータフロー

Data_OutputCleaned_DataItem_PipelineItemsSpiderSpider_MiddlewaresDownloader_MiddlewaresTarget_WebsiteInternetDownloaderSchedulerScrapy_EngineScrapy_Projectscrapy.cfgUserData_OutputCleaned_DataItem_PipelineItemsSpiderSpider_MiddlewaresDownloader_MiddlewaresTarget_WebsiteInternetDownloaderSchedulerScrapy_EngineScrapy_Projectscrapy.cfgUserConfigure ScrapyCreate Scrapy ProjectStart Scrapy EngineSchedule RequestsSend RequestsSend RequestsRequest WebsiteReturn ResponseReturn ResponseProcess ResponseProcess ResponseProcess ResponseGenerate ItemsProcess ItemsClean and Validate DataOutput Data

このシーケンス図は、Scrapyのデータフローを時系列に沿って表現しています。ユーザーがscrapy.cfgを設定し、Scrapyプロジェクトを作成すると、Scrapy Engineが起動します。Scrapy Engineはリクエストをスケジュールし、DownloaderがInternetを介してターゲットのウェブサイトにリクエストを送信します。ウェブサイトからのレスポンスは、DownloaderとMiddlewaresで処理された後、Spiderに渡されます。Spiderはレスポンスからアイテムを生成し、Item Pipelineに渡します。Item Pipelineはアイテムのクリーニングと検証を行い、最終的にクリーンアップされたデータが出力されます。

(見づらくて申し訳ないです🙇)

Spider

  • Spider の役割と動作 Spider は、Scrapy の中核となるコンポーネントです。Spider は、クロールする URL を定義し、ページから必要なデータを抽出するための解析ロジックを実装します。Spider は、レスポンスを処理し、新しいリクエストを生成することで、クローリングを継続的に行います。

  • Spider のサンプルコード

    import scrapy
    
    class MySpider(scrapy.Spider):
        name = 'myspider'
        start_urls = ['http://example.com']
    
        def parse(self, response):
            title = response.css('h1::text').get()
            description = response.xpath('//p[@class="description"]/text()').get()
    
            item = MyItem()
            item['title'] = title
            item['description'] = description
            yield item
    
            # 次のページへのリンクを辿る
            next_page = response.css('a.next-page::attr(href)').get()
            if next_page is not None:
                yield response.follow(next_page, callback=self.parse)
    

Items

  • Items の役割と定義方法 Items は、抽出したデータを構造化するためのコンテナです。Items を使用することで、データをフィールドごとに整理し、後処理を行うことができます。

  • Items のサンプルコード

    import scrapy
    
    class MyItem(scrapy.Item):
        title = scrapy.Field()
        description = scrapy.Field()
        price = scrapy.Field()
        image_urls = scrapy.Field()
    

Middlewares

  • Middlewares の種類と役割

    Spider MiddlewareSpiderDownloader MiddlewareDownloader

    Middlewares は、リクエストとレスポンスに対して処理を行うコンポーネントです。Downloader ミドルウェアは、リクエストの送信前とレスポンスの受信後に処理を行います。Spider ミドルウェアは、Spider の前後に処理を行います。

  • Downloader ミドルウェアのサンプルコード

    from scrapy import signals
    
    class MyDownloaderMiddleware:
        def process_request(self, request, spider):
            # リクエスト送信前の処理
            request.headers['User-Agent'] = 'MyCustomUserAgent'
            return None
    
        def process_response(self, request, response, spider):
            # レスポンス受信後の処理
            if response.status == 404:
                return None
            return response
    
  • Spider ミドルウェアのサンプルコード

    from scrapy import signals
    
    class MySpiderMiddleware:
        def process_spider_input(self, response, spider):
            # Spiderへのインプット処理
            return None
    
        def process_spider_output(self, response, result, spider):
            # Spiderからのアウトプット処理
            for item in result:
                if isinstance(item, dict):
                    # itemを加工する処理
                    item['custom_field'] = 'custom_value'
                yield item
    

Pipelines

  • Pipelines の役割と動作 Pipelines は、Spider から抽出されたデータに対して処理を行うコンポーネントです。Pipelines を使用することで、データのクリーニング、検証、重複除去、データベースへの保存などを行うことができます。

  • Pipelines のサンプルコード

    from scrapy.exceptions import DropItem
    
    class MyPipeline:
        def process_item(self, item, spider):
            if item['price'] is None:
                # 価格が存在しない場合はitemを破棄
                raise DropItem("Missing price")
            item['price'] = float(item['price'])
            return item
    

Settings

  • Settings の役割と主要な設定項目 Settings は、Scrapy の動作を制御するための設定ファイルです。Settings を使用することで、クローリングの動作、ミドルウェアの有効化、パイプラインの設定などを行うことができます。

  • Settings のサンプルコード

    # settings.py
    BOT_NAME = 'myproject'
    SPIDER_MODULES = ['myproject.spiders']
    NEWSPIDER_MODULE = 'myproject.spiders'
    
    ROBOTSTXT_OBEY = True
    
    ITEM_PIPELINES = {
        'myproject.pipelines.MyPipeline': 300,
    }
    
    DOWNLOAD_DELAY = 1
    

scrapy.cfg

  • scrapy.cfg の役割と設定方法 scrapy.cfg は、Scrapy プロジェクトの設定ファイルです。プロジェクトの設定、デプロイ設定などを定義します。scrapy.cfg は、プロジェクトのルートディレクトリに配置します。

    [settings]
    default = myproject.settings
    
    [deploy]
    url = http://localhost:6800/
    project = myproject
    

3. バンを回避するためのテクニック

バン回避の重要性

Web クローリングを行う際、一定の制限を超えてアクセスすると、Web サイトからバン(アクセス制限)される可能性があります。バンを回避することは、クローリングを継続的に行うために重要です。バンを回避するためには、IP アドレスの分散、User-Agent の変更、リクエストの間隔調整などのテクニックを用います。

IP アドレスの分散

  • IP アドレスを分散させる方法

    WebsiteProxy2Proxy1ScrapyWebsiteProxy2Proxy1Scrapyリクエストリクエストレスポンスレスポンスリクエストリクエストレスポンスレスポンス

    IP アドレスを分散させるために、複数の Proxy サーバーを利用します。リクエストを Proxy サーバーを経由して送信することで、Web サイトからは異なる IP アドレスからのアクセスとして認識されます。

  • Proxy の利用方法とサンプルコード

    # settings.py
    ROTATING_PROXY_LIST = [
        'username:password@ip:port',
        'username:password@ip:port',
        'username:password@ip:port',
    ]
    
    DOWNLOADER_MIDDLEWARES = {
        'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
        'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
    }
    

    scrapy-rotating-proxies というミドルウェアを使用することで、リクエストごとに Proxy を切り替えることができます。

User-Agent の変更

  • User-Agent を変更する理由 User-Agent は、クライアントがサーバーに送信する HTTP リクエストヘッダーの一部で、クライアントのソフトウェアやバージョンを識別するために使用されます。デフォルトの User-Agent を使用すると、Web サイトからボットとして認識される可能性があります。User-Agent を変更することで、通常のブラウザからのアクセスに見せかけることができます。

  • User-Agent を変更する方法とサンプルコード

    # settings.py
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'
    
    # middlewares.py
    from scrapy import signals
    
    class RotateUserAgentMiddleware:
        def process_request(self, request, spider):
            user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'
            request.headers['User-Agent'] = user_agent
    

    settings.py で User-Agent を設定するか、ミドルウェアでリクエストごとに User-Agent を変更することができます。

    また、scrapy-fake-useragentを利用するとランダムなUser-Agentでリクエストを送ることができます。

リクエストの間隔調整

  • リクエストの間隔を調整する理由 一定期間に大量のリクエストを送信すると、Web サイトから過剰なアクセスとみなされ、バンされる可能性があります。リクエストの間隔を調整することで、アクセスの頻度を抑えることができます。

  • リクエストの間隔を調整する方法とサンプルコード

    # settings.py
    DOWNLOAD_DELAY = 1.5  # リクエスト間の遅延を1.5秒に設定
    RANDOMIZE_DOWNLOAD_DELAY = True  # 遅延をランダム化
    
    # spider.py
    import scrapy
    from random import randint
    
    class MySpider(scrapy.Spider):
        name = 'myspider'
    
        def parse(self, response):
            # 次のリクエストを遅延してスケジュール
            delay = randint(1, 3)  # 1~3秒の遅延をランダムに生成
            yield scrapy.Request(next_url, callback=self.parse, dont_filter=True, meta={'download_delay': delay})
    

    settings.py で DOWNLOAD_DELAY を設定することで、リクエスト間の遅延を設定できます。また、リクエストごとに遅延を設定することもできます。

4. まとめ

Scrapy の利点と適した用途

Scrapy は、Python で書かれた強力な Web クローリングおよびスクレイピングフレームワークであり、以下のような利点と適した用途があります。

  1. 高速性と効率性 Scrapy は、非同期ネットワーク処理を利用しており、高速かつ効率的にクローリングを行うことができます。複数のリクエストを並行して処理することで、大量のデータを短時間で収集できます。

  2. 柔軟性と拡張性 Scrapy は、柔軟なアーキテクチャを持っており、開発者は自由にコンポーネントを追加・拡張することができます。ミドルウェアやパイプラインを使ってクローリングの動作をカスタマイズできるため、様々な要件に対応することができます。

  3. データの構造化 Scrapy では、Item を使ってスクレイピングしたデータを構造化することができます。データをフィールドごとに整理することで、後処理や保存が容易になります。

  4. 統合されたツール Scrapy には、Web クローラー、スパイダー、アイテムパイプラインなどの統合されたツールが用意されています。これらのツールを使うことで、クローリングに必要な機能を簡単に実装できます。

  5. 大規模クローリングに適している Scrapy は、大規模なクローリングプロジェクトに適しています。並行処理や分散処理により、大量のページを効率的にクロールすることができます。

Scrapy は、以下のような用途に適しています。

  • データ収集: Web サイトから情報を収集し、データ分析やマーケティングリサーチに活用する。
  • 価格比較: 複数の EC サイトから製品の価格情報を収集し、価格比較サービスを提供する。
  • ニュース収集: ニュースサイトから記事を収集し、ニュースアグリゲーションサービスを構築する。
  • 不動産情報の収集: 不動産サイトから物件情報を収集し、不動産検索サービスを提供する。

Scrapy を使う上での注意点

Scrapy を使ってクローリングを行う際は、以下の点に注意が必要です。

  1. 著作権と利用規約 クローリングを行う際は、対象の Web サイトの著作権や利用規約を確認し、違反しないように注意してください。無断でデータを収集することは避け、必要に応じて許可を得るようにしましょう。

  2. アクセス頻度の制限 Web サイトに過剰な負荷をかけないように、アクセス頻度を制限することが重要です。リクエストの間隔を適切に設定し、Web サイトのパフォーマンスに影響を与えないようにしてください。

  3. IP アドレスのブロック対策 大量のリクエストを短時間で送信すると、IP アドレスがブロックされる可能性があります。プロキシを使って IP アドレスを分散させたり、User-Agent を変更したりすることで、ブロックを回避することができます。

  4. データの品質管理 スクレイピングしたデータは、必ずしも完全ではありません。データの欠損や不整合がある場合があるため、データのクリーニングや検証が必要です。パイプラインを使ってデータの品質を管理しましょう。

  5. Web サイトの変更への対応 Web サイトのデザインや構造が変更された場合、スクレイピングのコードが動作しなくなることがあります。定期的にコードをメンテナンスし、変更に対応できるようにしておく必要があります。

Scrapy は、Web クローリングとスクレイピングを行う上で非常に強力なツールですが、適切に使用することが重要です。著作権や利用規約を遵守し、Web サイトに過剰な負荷をかけないように注意しながら、効率的にデータを収集していきましょう。

5. 参考

logo

Web Developer。パフォーマンス改善、データ分析基盤、生成AIに興味があり。Next.js, Terraform, AWS, Rails, Pythonを中心に開発スキルを磨いています。技術に関して幅広く投稿していきます。