Scrapy vs. Requests: ウェブスクレイピングにはどちらが適しているか?

ウェブスクレイピングにはScrapyとRequestsを比較し、ニーズに最適なツールを選択してください。
4 分読
Scrapy vs Requests blog image

このScrapy vs Requestsガイドでは、以下の内容を確認できます:

  • ScrapyとRequestsの概要
  • ウェブスクレイピングにおけるScrapyとRequestsの比較
  • ページネーションシナリオにおけるScrapyとRequestsの比較
  • ウェブスクレイピングシナリオにおけるScrapyとRequestsの共通制限事項

それでは始めましょう!

Requestsとは?

RequestsはHTTPリクエストを送信するためのPythonライブラリです。ウェブスクレイピングで広く使用され、通常はBeautifulSoupのようなHTMLパースライブラリと組み合わせて使われます。

ウェブスクレイピングにおけるRequestsの主な特徴は以下の通りです:

  • HTTPメソッドのサポートGETPOSTPUTPATCHDELETEなど主要なHTTPメソッドをすべて使用可能。ウェブページやAPIとのやり取りに不可欠です。
  • カスタムヘッダー:カスタムヘッダー(例:User-Agentなど)を設定し、実際のブラウザを模倣したり基本認証を処理したりできます。
  • セッション管理:requests.Session()オブジェクトにより、複数のリクエスト間でクッキーやヘッダーを永続化できます。これはログインが必要なウェブサイトのスクラッピングやセッション状態の維持に有用です。
  • タイムアウトとエラー処理: リクエストの停止を回避するためのタイムアウト設定や、堅牢なスクレイピングのための例外処理が可能です。
  • プロキシサポート: リクエストをプロキシ経由でルーティングでき、IP禁止の回避や地域制限コンテンツへのアクセスに有効です。

Scrapyとは?

ScrapyはPythonで書かれたオープンソースのウェブスクレイピングフレームワークです。ウェブサイトからデータを高速・効率的・スケーラブルに抽出するために設計されています。

Scrapyは、ウェブサイトのクロール、データ抽出、および様々な形式(JSON、CSVなど)での保存のための完全なフレームワークを提供します。複雑なクロールタスクや同時リクエストを処理しながらクロールルールを順守できるため、大規模なウェブスクレイピングプロジェクトに特に有用です。

ウェブスクレイピングにおけるScrapyの主な機能は以下の通りです:

  • 組み込みのウェブクローリング機能: Scrapyはウェブクローラーとして設計されています。これにより、ウェブページ上のリンクを自動的に追跡でき、最小限の労力で複数ページやサイト全体をウェブスクレイピングできます。
  • 非同期リクエスト:非同期アーキテクチャを採用し、複数のリクエストを同時に処理します。これにより、requestsなどのPythonHTTPクライアントよりも大幅に高速です。
  • データ抽出用セレクター:ScrapyはXPathやCSSセレクターを用いたHTMLからのデータ抽出機能を提供します。
  • カスタマイズ用ミドルウェア: リクエストとレスポンスの処理方法をカスタマイズするためのミドルウェアをサポートしています。
  • 自動スロットリング:対象サーバーへの負荷を避けるため、リクエストを自動的に制限します。サーバーの応答時間や負荷に基づいてクロール速度を調整可能です。
  • robots.txtの処理ウェブスクレイピングにおいてrobots.txtファイルを尊重し、スクレイピング活動がサイトのルールに準拠することを保証します。
  • プロキシとユーザーエージェントのローテーション:Scrapyはミドルウェアを通じてプロキシローテーションユーザーエージェントローテーションをサポートし、IP禁止や検出を回避するのに役立ちます。

Scrapy vs Requests: ウェブスクレイピングにおける機能比較

RequestsとScrapyの基礎を理解したところで、ウェブスクレイピングにおける両者の用途を詳細に比較します:

機能 Scrapy Requests
使用例 大規模かつ複雑なスクレイピングプロジェクト より単純なウェブスクレイピングタスクとプロトタイプ
非同期リクエスト 非同期リクエストの組み込みサポート 組み込みサポートなし
クロール リンクを自動的に追跡し、複数のページをクロール クロールには手動実装が必要
データ抽出 XPathおよびCSSセレクタの組み込みサポート データ抽出の管理には外部ライブラリが必要
同時実行 複数のリクエストを同時に処理する 外部統合が必要(並行リクエスト管理用)
ミドルウェア プロキシ、再試行、ヘッダー処理用のカスタマイズ可能なミドルウェア 組み込みミドルウェアなし
スロットリング サーバーの過負荷を回避するための組み込み自動スロットリング 組み込みスロットリングなし
プロキシローテーション ミドルウェア経由のプロキシローテーションをサポート 手動実装が必要
エラー処理 失敗したリクエストに対する組み込みリトライ機構 手動実装が必要
ファイルダウンロード ファイルダウンロードをサポートするが、追加設定が必要 シンプルでわかりやすいファイルダウンロードサポート

ユースケース

Scrapyは大規模かつ複雑なスクレイピングプロジェクト向けの完全なウェブスクレイピングフレームワークです。複数のページをクロールするタスク、同時リクエスト、構造化された形式でのデータエクスポートに最適です。

一方、RequestsはHTTPリクエストを管理するライブラリです。そのため、単一ウェブページの取得、APIとのやり取り、ファイルダウンロードといった単純なタスクに適しています。

非同期リクエストと並行処理

ScrapyはPython用イベント駆動型ネットワーキングフレームワークTwistedを基盤としています。これにより非同期リクエストや複数リクエストの同時処理が可能となり、大規模スクレイピングにおいて大幅な高速化を実現します。

一方、Requestsは非同期リクエストや並行リクエストをネイティブでサポートしていません。非同期HTTPリクエストを実行したい場合は、GRequestsと統合する必要があります。

クローリング

ROBOTSTXT_OBEY設定がTrueに設定されている場合、Scrapyはrobots.txtファイルを読み取り、ウェブページ上の許可されたリンクを自動的に追跡し、許可されたページをクロールします。

Requests には組み込みのクロール機能がないため、リンクを手動で定義し、追加のリクエストを行う必要があります。

データ抽出

Scrapy はXPath および CSS セレクタを使用したデータ抽出を組み込みでサポートしており、HTML および XML のパースを容易にします。

Requests にはデータ抽出機能は含まれていません。データのパースと抽出には、BeautifulSoup などの外部ライブラリを使用する必要があります。

ミドルウェア

Scrapyはプロキシ処理、再試行ヘッダー設定などに対応するカスタマイズ可能なミドルウェアを提供します。これにより高度なスクレイピングタスクへの拡張性が非常に高くなります。

一方、Requests はミドルウェアをサポートしていないため、プロキシローテーションやリトライなどの機能は手動で実装する必要があります。

スロットリング

Scrapyには、サーバーの応答時間や負荷に基づいてクロール速度を調整する自動スロットリング機能が組み込まれています。これにより、ターゲットサーバーへのHTTPリクエストの集中を回避できます。

Requestsには組み込みのスロットリング機能がありません。スロットリングを実装したい場合は、例えばtime.sleep()メソッドを使用するなどして、リクエスト間に手動で遅延を追加する必要があります。

プロキシローテーション

Scrapyはミドルウェアを介したプロキシローテーションをサポートしており、IP禁止を回避し匿名でサイトをスクレイピングすることが容易です。

Requestsにはプロキシローテーション機能が組み込まれていません。Requestsでプロキシを管理するには、ガイドで説明されているように、プロキシを手動で設定しカスタムロジックを記述する必要があります。

エラー処理

Scrapyにはリクエスト失敗時のリトライ機能が組み込まれており、ネットワークエラーやサーバー問題への堅牢な対応が可能です。

一方、Requestsではエラーや例外を手動で処理する必要があります(例:try-exceptブロックの使用)。retry-requestsなどのライブラリも検討してください。

ファイルダウンロード

Scrapy はFilesPipelineによるファイルダウンロードをサポートしていますが、大きなファイルやストリーミングを扱うには追加の設定が必要です。

Requestsは、requests.get()メソッドにstream=Trueパラメータを指定するだけで、シンプルで直感的なファイルダウンロード機能を提供します。

Scrapy vs Requests:ページネーションシナリオにおける両ライブラリの比較

RequestsとScrapyの概要は理解できたでしょう。特定のウェブスクレイピングシナリオにおけるステップバイステップのチュートリアル比較をご覧ください!

焦点は、ページネーションシナリオにおけるこれら2つのライブラリの比較を示すことに置かれます。ウェブスクレイピングにおけるページネーションの処理には、リンク追跡と複数ページにわたるデータ抽出のためのカスタムロジックが必要です。

対象サイトは「Quotes to Scrape」とし、著名作家の引用文を異なるページで提供しています:

The Quotes to Scrape target site

このチュートリアルの目的は、ScrapyとRequestsを使用して全ページから引用文を取得する方法を示すことです。RequestsはScrapyよりも複雑な場合があるため、まずRequestsから始めます。

環境要件

ScrapyとRequestsのチュートリアルを再現するには、Python 3.7以上がマシンにインストールされている必要があります。

Requestsを使用したウェブスクレイピングの方法

この章では、Requestsを使用して対象サイトからすべての引用文をスクレイピングする方法を学びます。

注意点として、Requests単体ではウェブページから直接データをスクレイピングできません。BeautifulSoupのようなHTMLパーサーも必要です。

ステップ #1: 環境設定と依存関係のインストール

プロジェクトのメインフォルダをrequests_scraper/としますこのステップ終了時、フォルダ構造は以下のようになります:

requests_scraper/
    ├── requests_scraper.py
    └── venv/

ここで:

  • requests_scraper.py:すべてのコードを含むPythonファイル
  • venv/は仮想環境を含みます

venv/仮想環境ディレクトリは以下のように作成できます:

python -m venv venv

Windowsで有効化するには、以下を実行します:

venvScriptsactivate

macOS および Linux では、同等の操作として以下を実行します:

source venv/bin/activate

これで必要なライブラリをインストールできます:

pip install requests beautifulsoup4

ステップ #2: 変数の設定

これで、requests_scraper.pyファイルにコードを書き始める準備が整いました。

まず、変数を以下のように設定します:

base_url = "https://quotes.toscrape.com"
all_quotes = []

ここで定義した内容:

  • base_url: スクラップ対象ウェブサイトの開始URL
  • all_quotes:スクレイピングで取得したすべての引用を格納する空のリスト

ステップ #3: スクレイピングロジックの作成

以下のコードでスクレイピングとクローリングのロジックを実装できます:

url = base_url
while url:
    # 現在のページにGETリクエストを送信
    response = requests.get(url)

    # ページのHTMLコードをパース
    soup = BeautifulSoup(response.text, "html.parser")

    # 引用ブロックを全て検索
    quotes = soup.select(".quote")
    for quote in quotes:
        text = quote.select_one(".text").get_text(strip=True)
        author = quote.select_one(".author").get_text(strip=True)
        tags = [tag.get_text(strip=True) for tag in quote.select(".tag")]
        all_quotes.append({
            "text": text, 
            "author": author, 
            "tags": ",".join(tags)
        })

    # 「次へ」ボタンを確認
    next_button = soup.select_one("li.next")
    if next_button:
        # 「次へ」ボタンからURLを抽出し、
        # 次のスクレイピング対象ページとして設定
        next_page = next_button.select_one("a")["href"]
        url = base_url + next_page
    else:
        url = None

このコードは:

  • すべてのページがスクレイピングされるまで継続するwhileループをインスタンス化します
  • whileループ内部:
    • soup.``select``()はページ上のすべての引用HTML要素を捕捉します。ページのHTML構造では、各引用要素が「quote」というクラスを持っています。
    • forループはquoteクラスを全て巡回し、Beautiful Soupのスクレイピングメソッドを用いて引用文からテキスト、著者、タグを抽出します。ここで、各引用要素が複数のタグを含む可能性があるため、タグ処理にはカスタムロジックが必要です。
      The ‘quote’ classes in the HTML code of the target web page
  • ページ全体のスクレイピング後、スクリプトは「次へ」ボタンを検索します。ボタンが存在する場合、次のページへのリンクを抽出します。その後、変数`url = base_url + next_page` を通じてベースURLを次のページに更新します。最終ページに到達すると、次のURLは`None` に設定され、処理が終了します。
The ‘next’ class that defines the ‘next’ button in the HTML code of the target web page

ステップ #4: データをCSVファイルに追加

すべてのデータをスクレイピングしたら、以下のようにCSVファイルに追加できます:

with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
    writer.writeheader()
    writer.writerows(all_quotes)

このスクリプトのこの部分では、csvライブラリを使用して以下の処理を行います:

  • 出力CSVファイル名をquotes.csvと指定します。
  • CSVを書き込みモード(mode="w")で開く。
    • CSVにヘッダー行を書き込み
    • スクレイピングした引用文を全てファイルに書き込む

ステップ #5: 全てを統合する

チュートリアルのScrapy vs Requests部分の全コードは以下の通りです:

import requests
from bs4 import BeautifulSoup
import csv

# ウェブサイトのURL
base_url = "https://quotes.toscrape.com"
# 全ての引用文を格納するリスト
all_quotes = []

# 最初のページからスクレイピングを開始
url = base_url
while url:
    # 現在のページにGETリクエストを送信
    response = requests.get(url)

    # ページのHTMLコードをパース
    soup = BeautifulSoup(response.text, "html.parser")

    # 全ての引用ブロックを検索
    quotes = soup.select(".quote")
    for quote in quotes:
        text = quote.select_one(".text").get_text(strip=True)
        author = quote.select_one(".author").get_text(strip=True)
        tags = [tag.get_text(strip=True) for tag in quote.select(".tag")]
        all_quotes.append({
            "text": text, 
            "author": author, 
            "tags": ",".join(tags)
        })

    # 「次へ」ボタンを確認
    next_button = soup.select_one("li.next")
    if next_button:
        # 「次へ」ボタンからURLを抽出し、
        # 次のスクレイピング対象ページに設定
        next_page = next_button.select_one("a")["href"]
        url = base_url + next_page
    else:
        url = None

# 引用文をCSVファイルに保存
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
    writer.writeheader()
    writer.writerows(all_quotes)

上記スクリプトを実行:

python requests_scraper.py

プロジェクトフォルダにquotes.csvファイルが生成されます:

The expected CSV file after data extraction with Requests and BeautifulSoup

Scrapyを使ったウェブスクレイピングの方法

Requestsを使用したウェブスクレイピングの方法を学んだので、同じ対象ページと目的でScrapyを使用する方法を見てみましょう。

ステップ #1: 環境設定と依存関係のインストール

プロジェクトのメインフォルダをscrapy_scraper/と命名するとします

まず、前述の手順に従って仮想環境を作成・有効化し、Scrapyをインストールします:

pip install scrapy

quotes_scraper/内に事前定義されたファイルを生成するため、Scrapyを起動します:

scrapy startproject quotes_scraper

プロジェクトの構造は以下のようになります:

scrapy_scraper/ 
├── quotes_scraper/ # Scrapyプロジェクトのメインフォルダ
│   ├── __init__.py  
│   ├── items.py # スクレイピング対象アイテムのデータ構造を定義
│   ├── middlewares.py # カスタムミドルウェア
│   ├── pipelines.py # スクレイピングデータのポスト処理を処理
│   ├── settings.py # プロジェクト設定
│   └── spiders/ # 全てのスパイダー用フォルダ
├── venv/
└── scrapy.cfg # Scrapy設定ファイル

ステップ #2: Items の定義

items.pyファイルは、スクレイピングしたいデータの構造を定義します。引用文、著者、タグを取得したいので、以下のように定義します:

import scrapy

class QuotesScraperItem(scrapy.Item):
    quote = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

ステップ #3: メインスパイダーを定義

spiders/フォルダ内に以下のPythonファイルを作成します:

  • __init__.py(ディレクトリをPythonパッケージとしてマーク)
  • quotes_spider.py

quotes_spider.pyには実際のスクレイピングロジックが含まれます:

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import QuotesScraperItem

class QuotesSpider(CrawlSpider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ["https://quotes.toscrape.com/"]

    # ページネーションリンク追跡ルール定義
    rules = (
        Rule(LinkExtractor(restrict_css="li.next a"), callback="parse_item", follow=True),
    )

    def parse_item(self, response):
        # 引用文・著者・タグを抽出
        for quote in response.css("div.quote"):
            item = QuotesScraperItem()
            item["quote"] = quote.css("span.text::text").get()
            item["author"] = quote.css("small.author::text").get()
            item["tags"] = quote.css("div.tags a.tag::text").getall()
            yield item

上記のスニペットは、以下の処理を行うQuotesSpider()クラスを定義します:

  • スクレイピング対象のURLを定義します。
  • Rule()クラスでページネーションのルールを定義し、クローラーが全ての次ページを追跡できるようにする。
  • parse_item()メソッドで引用文、著者、タグを抽出します。

ステップ #4: 設定の定義

データをCSVに追加するには、Scrapyで特別な設定が必要です。設定を行うには、settings.pyファイルを開き、以下の変数を追加します:

FEED_FORMAT = "csv"  
FEED_URI = "quotes.csv" 

これらの設定の役割は以下の通りです:

ステップ #5: クローラーの実行

前の手順で言及されていないPythonファイルは、このチュートリアルでは不要です。デフォルトデータのままにしておいてください。

クローラーを起動するには、quotes_scraper/フォルダに移動します:

cd quotes_scraper

次にクローラーを実行します:

scrapy crawl quotes

このコマンドは、ファイルquotes_spider.py 内のクラスQuotesSpider()をインスタンス化します。これがクローラーを起動するクラスです。最終的に得られるCSVファイルは、RequestsとBeautifulSoupで取得したものと全く同じです!

この例が示すのは:

  • Scrapyの性質上、大規模プロジェクトに適している理由
  • ページネーションの管理がScrapyでは容易であること(カスタムロジックを記述する必要がなく、ルールを管理するだけで済むため)。
  • ScrapyではCSVファイルへのデータ追加がより簡便であること。これは、Pythonスクリプトで同様の処理を行う際に作成する従来のカスタムロジックではなく、設定を2つ追加するだけで済むためです。

ScrapyとRequestsの共通する制限事項

ScrapyとRequestsはウェブスクレイピングプロジェクトで広く使用されていますが、いくつかの欠点も伴います。

具体的には、あらゆるスクレイピングライブラリやフレームワークが直面する共通の制限の一つがIP禁止です。Scrapyにはサーバーへのリクエスト速度を調整するスロットリング機能が備わっていることを学びましたが、それでもIP禁止を回避するには不十分な場合が多くあります。

IPが禁止されるのを避ける解決策は、コードにプロキシを実装することです。その方法を見てみましょう!

Requestsでのプロキシ使用

Requestsで単一のプロキシを使用する場合は、以下のロジックを使用します:

 プロキシ = {
     "http": "<HTTP_PROXY_URL>",
     "https": "<HTTPS_PROXY_URL>"
 }
 response = requests.get(url, proxies=proxy)

Requestsにおけるプロキシとプロキシローテーションの詳細については、当ブログのガイドをご覧ください:

Scrapyでのプロキシ使用方法

コードに単一のプロキシを実装したい場合は、settings.pyファイルに以下の設定を追加してください:

# 単一プロキシの設定
HTTP_PROXY = "<PROXY_URL>"

# HttpProxyMiddlewareを有効化し、デフォルトのUserAgentMiddlewareを無効化
DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

これらの設定により、すべてのリクエストが指定されたプロキシを経由します。詳細はScrapyプロキシ統合ガイドをご覧ください。

代わりにローテーションプロキシを実装したい場合は、scrapy-rotating-proxiesライブラリを使用できます。同様に、自動ローテーションするレジデンシャルプロキシも利用可能です。

信頼性の高いプロキシをお探しの場合は、Bright Dataのプロキシネットワークがフォーチュン500企業や世界中の20,000社以上の顧客から信頼されていることをご留意ください。この広範なネットワークには以下が含まれます:

結論

このScrapy vs Requestsブログ記事では、ウェブスクレイピングにおける両ライブラリの役割について学びました。ページ取得とデータ抽出の機能を探り、実際のページネーションシナリオにおけるパフォーマンスを比較しました。

Requests は手動でのロジック設定が必要ですが、カスタムユースケースに対してより高い柔軟性を提供します。一方、Scrapy は適応性がやや劣りますが、構造化されたスクレイピングに必要なツールの大半を提供します。

また、IP禁止の可能性や地域制限コンテンツの問題など、両ライブラリの限界も明らかになりました。幸い、プロキシやBrightDataのWeb Scrapersのような専用のウェブスクレイピングソリューションを使用することで、これらの課題を克服できます。

スクレイパーはScrapyとRequestsの両方にシームレスに統合され、主要ウェブサイトから制限なく公開データを抽出できます。

Bright Dataの無料アカウントを作成し、プロキシとスクレイパーAPIを今すぐお試しください!無料トライアルを開始しましょう!