Scrapy vs Playwright:ウェブスクレイピングの比較

ScrapyとPlaywrightの効果的なWebスクレイピングの違いやメリットをご紹介します。
6 分読
Scrapy vs Playwright blog image

このガイドで、あなたは学ぶだろう:

  • Scrapyとは
  • 劇作家とは
  • ウェブスクレイピングのために各社が提供する機能とその比較
  • 両ツールを使ったウェブスクレイピング入門
  • Playwrightを使ったスクレイパーの作り方
  • Scrapyを使ったウェブスクレイピングスクリプトの作り方
  • ウェブスクレイピングに適したツールは?
  • 一般的な制約とその克服法

さあ、飛び込もう!

Scrapyとは?

ScrapyはPythonで書かれたオープンソースのウェブスクレイピングフレームワークで、効率的なデータ抽出のために開発された。並列リクエスト、リンクフォロー、JSONやCSVといったフォーマットでのデータエクスポートといった機能をビルトインでサポートしている。また、ミドルウェア、プロキシの統合、リクエストの自動再試行などの機能も備えている。Scrapyは非同期かつ静的なHTMLページ上で動作する。

劇作家とは何か?

Playwrightは、ブラウザでのE2EテストとWebスクレイピングのためのオープンソースの自動化フレームワークである。Chrome、Firefox、WebKitなど複数のブラウザをサポートしており、それぞれヘッドレスモードとヘッドレスモードの両方で動作する。また、ブラウザ自動化APIは、TypeScript/JavaScript、Python、Java、C#など、複数のプログラミング言語で利用できる。

ScrapyとPlaywrightの比較:ウェブ・スクレイピングのための機能比較

ScrapyとPlaywrightを、優れたウェブ・スクレイピング・ツールとするための5つの異なる側面から比較してみよう。

他の対決ブログ記事はこちら:

さて、ScrapyとPlaywrightの比較を始めよう!

セットアップと設定の容易さ

Scrapyは、必要最小限の構成で簡単なセットアップを提供します。組み込みのCLIにより、プロジェクトの作成、スパイダーの定義、データのエクスポートを素早く行うことができる。逆に、Playwrightは、ブラウザの依存関係をインストールし、適切な設定をチェックする必要があるため、より多くのセットアップが必要です。

学習曲線

Scrapyは、モジュール構造、豊富な機能、ユニークな設定のため、初心者にとっては学習曲線が急です。スパイダー、ミドルウェア、パイプラインなどの概念を理解するには時間がかかる。Playwrightは、そのAPIがブラウザ自動化の知識がある人にとって馴染み深いものであるため、より簡単に使い始めることができる。

ダイナミック・コンテンツ・ハンドリング

Scrapyは静的なHTMLドキュメントしか扱えないため、JavaScriptを使用するウェブサイトでは苦労する。動的コンテンツを扱うことは可能だが、Splashや同様のツールと統合する必要がある。Playwrightはブラウザ上でネイティブにページをレンダリングするため、動的コンテンツやJavaScriptでレンダリングされたコンテンツの扱いに優れている。つまり、React、Angular、Vueのようなクライアントフレームワークに依存するページのスクレイピングに使用できる。

カスタマイズと拡張性

Scrapyは、ミドルウェア、拡張機能、パイプラインのサポートを通じて、高度なカスタマイズオプションを提供する。また、いくつかのプラグインやアドオンも利用できる。一方、Playwrightはネイティブで拡張可能ではない。幸いなことに、コミュニティはPlaywright Extraプロジェクトでこの制限に対処している。

その他のスクレイピング機能

Scrapyは、プロキシ統合、自動再試行、設定可能なデータエクスポートなどの組み込み機能を備えています。また、IPローテーションやその他の高度なシナリオのための統合メソッドも提供している。Playwrightは、プロキシ統合やその他の主要なスクレイピング機能をサポートしている。そのため、同じ結果を得るには、Scrapyに比べて手作業が必要になる。

PlaywrightとScrapyの比較:スクレイピングスクリプトの比較

以下の2つのセクションでは、PlaywrightとScrapyを使って同じサイトをスクレイピングする方法を学びます。PlaywrightはScrapyのようにウェブスクレイピングに最適化されていないので、少し時間がかかるかもしれません。

対象サイトはBooks to Scrapeスクレイピング・サンドボックスとなる:

ターゲット・サイト

両スクレイパーの目的は、サイトからファンタジーの本をすべて取得することで、それにはページネーションの処理が必要である。

Scrapyはページを静的なものとして扱い、HTMLドキュメントを直接解析する。代わりに、Playwrightはブラウザでそれらをレンダリングし、ページ上の要素と対話し、ユーザーのアクションをシミュレートします。

ScrapyスクリプトはPythonで書かれ、PlaywrightスクリプトはJavaScriptで書かれる。それでも、同じAPIを公開するplaywright-pythonライブラリを使用することで、PlaywrightのJavaScriptスクリプトを簡単にPythonに変換することができます。

どちらの場合も、スクリプトの最後には、Books to Scrapeからファンタジーブックの詳細をすべて含むCSVが作成されます。

さて、PlaywrightとScrapyのスクレイピング比較に飛び込もう!

ウェブスクレイピングにPlaywrightを使う方法

以下の手順に従って、Playwrightを使ってJavaScriptで簡単なウェブスクレイピングスクリプトを書いてみましょう。Playwrightを使ったウェブスクレイピングのガイドをお読みください。

ステップ1:プロジェクトのセットアップ

始める前に、最新バージョンのNode.jsがローカルにインストールされていることを確認してください。インストールされていない場合は、ダウンロードしてインストール・ウィザードに従ってください。

次に、Playwrightスクレイパー用のフォルダを作成し、ターミナルを使ってその中に移動する:

mkdir playwright-scraper
cd playwright-scraper

playwright-scraperフォルダ内で、npmプロジェクトを初期化する:

npm init -y

お気に入りのJavaScript IDEでplaywright-scraperフォルダを開く。IntelliJ IDEAや Visual Studio Codeは素晴らしい選択肢だ。そのフォルダーの中にscript.jsファイルを作成する:

Playwrightスクレイピング・プロジェクト・ファイル構造

素晴らしい!これでPlaywrightを使ったNode.jsでのWebスクレイピングの準備は完了です。

ステップ2:Playwrightのインストールと設定

プロジェクトフォルダで以下のコマンドを実行し、Playwrightをインストールする:

npm install playwright

次に、ブラウザと追加の依存関係をインストールしてください:

npx playwright install

script.jsを開き、以下のコードを追加して、Playwrightをインポートし、Chromiumブラウザのインスタンスを起動する:

const { chromium } = require("playwright");

(async () => {
  // initialize a Chromium browser
  const browser = await chromium.launch({
    headless: false, // comment out in production
  });

  // scraping logic goes here...

  // close the browser and release resources
  await browser.close();
})();

headless: falseオプションを指定すると、ブラウザがヘッドモードで起動します。これにより、スクリプトが何をしているかを見ることができる。

ステップ#3: ターゲットページに接続する

ブラウザで新しいページを初期化し、goto()関数を使って目的のページに移動する:

const page = await browser.newPage();
await page.goto("https://books.toscrape.com/catalogue/category/books/fantasy_19/index.html");

close()関数の前にブレークポイントを設定してデバッガでスクリプトを実行すると、ブラウザが開き、ターゲット・ページに移動するのがわかります:

Playwrightによって開かれたChromiumブラウザのウィンドウ

驚いた!Playwrightは期待通りにブラウザをコントロールしている。

ステップ#4:データ解析ロジックの実装

スクレイピング・ロジックを書く前に、ページ構造を理解する必要がある。そのためには、ブラウザのシークレットウィンドウでターゲットサイトを開く。そして、ブックエレメントの上で右クリックし、”Inspect “オプションを選択する。

これがDevToolsに表示されるはずのものだ:

ブック要素のDevToolsセクション

上では、.product_pod CSSセレクタを使って各ブック要素を選択できることがわかります。

ページには複数のブックが含まれているので、まずスクレイピングされたデータを格納する配列を初期化します:

books = []

それらをすべて選択し、以下のように反復処理する:

const bookElements = await page.locator(".product_pod").all();
for (const bookElement of bookElements) {
  // extract book details...
}

上の画像のように、それぞれのブックエレメントから、抽出することができます:

  • <a>タグの本のURL
  • h3 aノードの書名
  • .thumbnail要素のブック画像
  • .star-rating要素からの本の評価
  • .product_price .price_color要素の商品価格。
  • .availability要素による商品の在庫状況

次に、スクレイピング・ロジックをループ内に実装する:

const urlElement = await bookElement.locator("a").first();
const url = makeAbsoluteURL(
  await urlElement.getAttribute("href"),
  "https://books.toscrape.com/catalogue/"
);

const titleElement = await bookElement.locator("h3 a");
const title = await titleElement.getAttribute("title");

const imageElement = await bookElement.locator(".thumbnail");
const image = makeAbsoluteURL(
  await imageElement.getAttribute("src"),
  "https://books.toscrape.com/"
);

const ratingElement = await bookElement.locator(".star-rating");
const ratingClass = await ratingElement.getAttribute("class");
let rating;
switch (true) {
  case ratingClass.includes("One"):
    rating = 1;
    break;
  case ratingClass.includes("Two"):
    rating = 2;
    break;
  case ratingClass.includes("Three"):
    rating = 3;
    break;
  case ratingClass.includes("Four"):
    rating = 4;
    break;
  case ratingClass.includes("Five"):
    rating = 5;
    break;
  default:
    rating = null;
}

const priceElement = await bookElement.locator(
  ".product_price .price_color"
);
const price = (await priceElement.textContent()).trim();

const availabilityElement = await bookElement.locator(".availability");
const availability = (await availabilityElement.textContent()).trim();

上記のスニペットでは、getAttribute()textContent()Playwright 関数を使用して、それぞれ HTML ノードから特定の HTML 属性とテキストを抽出しています。評価スコアを取得するカスタムロジックに注意してください。

さらに、ページ上のURLは相対URLなので、以下のカスタム関数を使用して絶対URLに変換することができる:

function makeAbsoluteURL(url, baseURL) {
  // use a regular expression to remove any ../ or ../../ patterns
  const cleanURL = url.replace(/(\.\.\/)+/, "");

  // combine the base URL with the cleaned relative URL
  return baseURL + cleanURL;
}

次に、スクレイピングされたデータを新しいオブジェクトに入力し、それをbooks配列に追加します:

const book = {
  "url": url,
  "title": title,
  "image": image,
  "rating": rating,
  "price": price,
  "availability": availability,
};
books.push(book);

完璧だ!これでPlaywrightのスクレイピング・ロジックは完成だ。

ステップ4:クローリングロジックの実装

対象のサイトを見てみると、いくつかのページには一番下に「次へ」のボタンがあることに気づくだろう:

次へ」ボタンのHTMLコード

クリックすると次のページがロードされる。最後のページ分割ページには、明らかな理由で含まれていないことに注意してください。

このように、ウェブクローリングのロジックはwhile(true)ループで実装することができる:

  1. 現在のページからデータをスクレイピングする
  2. 次へ」ボタンがあればそれをクリックし、新しいページがロードされるのを待つ。
  3. 次へ」ボタンが見つからなくなるまで処理を繰り返す

以下はその方法である:

while (true) {
  // select the book elements ...

  // select the "next" button and check if it is on the page
  const nextElement = await page.locator("li.next a");
  if ((await nextElement.count()) !== 0) {
    // click the "next" button and go to the next page
    await nextElement.click();
    // wait for the page to have been loaded
    await page.waitForLoadState("domcontentloaded")
  } else {
    break;
  }
}

素晴らしい!クロール・ロジックが実装された。

ステップ5:CSVにエクスポート

最後のステップは、スクレイピングしたデータをCSVファイルにエクスポートすることだ。Node.jsを使ってもできますが、fast-csvのような専用ライブラリを使えばもっと簡単です。

以下のコマンドを実行してfast-csvパッケージをインストールする:

npm install fast-csv

scraping.jsファイルの冒頭で、必要なモジュールをインポートする:

const { writeToPath } = require("fast-csv");

次に、以下のスニペットを使って、スクレイピングしたデータをCSVファイルに書き出す:

writeToPath("books.csv", books, { headers: true });

やった!Playwrightウェブ・スクレイピング・スクリプトの準備ができた。

ステップ6:すべてをまとめる

script.jsファイルには、以下を記述する:

const { chromium } = require("playwright");
const { writeToPath } = require("fast-csv");

(async () => {
  // initialize a Chromium browser
  const browser = await chromium.launch({
    headless: false, // comment out in production
  });

  // initialize a new page in the browser
  const page = await browser.newPage();

  // visit the target page
  await page.goto(
    "https://books.toscrape.com/catalogue/category/books/fantasy_19/index.html"
  );

  // where to store the scraped data
  books = [];

  while (true) {
    // select the book elements
    const bookElements = await page.locator(".product_pod").all();
    // iterate over them to extract data from them
    for (const bookElement of bookElements) {
      // data extraction logic
      const urlElement = await bookElement.locator("a").first();
      const url = makeAbsoluteURL(
        await urlElement.getAttribute("href"),
        "https://books.toscrape.com/catalogue/"
      );

      const titleElement = await bookElement.locator("h3 a");
      const title = await titleElement.getAttribute("title");

      const imageElement = await bookElement.locator(".thumbnail");
      const image = makeAbsoluteURL(
        await imageElement.getAttribute("src"),
        "https://books.toscrape.com/"
      );

      const ratingElement = await bookElement.locator(".star-rating");
      const ratingClass = await ratingElement.getAttribute("class");
      let rating;
      switch (true) {
        case ratingClass.includes("One"):
          rating = 1;
          break;
        case ratingClass.includes("Two"):
          rating = 2;
          break;
        case ratingClass.includes("Three"):
          rating = 3;
          break;
        case ratingClass.includes("Four"):
          rating = 4;
          break;
        case ratingClass.includes("Five"):
          rating = 5;
          break;
        default:
          rating = null;
      }

      const priceElement = await bookElement.locator(
        ".product_price .price_color"
      );
      const price = (await priceElement.textContent()).trim();

      const availabilityElement = await bookElement.locator(".availability");
      const availability = (await availabilityElement.textContent()).trim();

      // populate a new book item with the scraped data and
      // then add it to the array
      const book = {
        "url": url,
        "title": title,
        "image": image,
        "rating": rating,
        "price": price,
        "availability": availability,
      };
      books.push(book);
    }

    // select the "next" button and check if it is on the page
    const nextElement = await page.locator("li.next a");
    if ((await nextElement.count()) !== 0) {
      // click the "next" button and go to the next page
      await nextElement.click();
      // wait for the page to have been loaded
      await page.waitForLoadState("domcontentloaded");
    } else {
      break;
    }
  }

  // export the scraped data to CSV
  writeToPath("books.csv", books, { headers: true });

  // close the browser and release resources
  await browser.close();
})();

function makeAbsoluteURL(url, baseURL) {
  // use a regular expression to remove any ../ or ../../ patterns
  const cleanURL = url.replace(/(\.\.\/)+/, "");

  // combine the base URL with the cleaned relative URL
  return baseURL + cleanURL;
}

このNode.jsコマンドで起動する:

node script.js

結果は次のようなbooks.csvファイルになる:

出力CSVファイル

ミッション完了!さて、次はScrapyを使って同じ結果を得る方法を見てみよう。

ウェブスクレイピングのためのScrapyの使い方

以下の手順に従って、Scrapyでシンプルなウェブスクレーパーを構築する方法をご覧ください。より詳しいガイダンスについては、Scrapyウェブスクレイピングのチュートリアルをご覧ください。

ステップ1:プロジェクトのセットアップ

始める前に、ローカルにPython 3がインストールされていることを確認してください。なければ、公式サイトからダウンロードしてインストールしてください。

プロジェクト用のフォルダを作成し、その中に仮想環境を初期化する:

mkdir scrapy-scraper
cd scrapy-scraper
python -m venv venv

Windowsでは、以下のコマンドを実行して環境をアクティブにする:

venv\Scripts\activate

同様に、UnixやmacOSでは、以下を実行する:

source venv/bin/activate

アクティベートされた環境で、Scrapyをインストールする:

pip install scrapy

次に、以下のコマンドを実行して、”books_scraper “というScrapyプロジェクトを作成する:

scrapy startproject books_scraper

甘い!これでScrapyを使ったウェブスクレイピングの準備は完了だ。

ステップ2:Scrapy Spiderの作成

Scrapyプロジェクトフォルダに入り、ターゲットサイトの新しいスパイダーを生成します:

cd books_scraper
scrapy genspider books books.toscrape.com

Scrapyは自動的に必要なファイルを作成します。具体的には、books_scraperディレクトリには以下のようなファイル構造になっているはずです:

books_scraper/
   │── __init__.py
   │── items.py
   │── middlewares.py
   │── pipelines.py
   │── settings.py
   └── spiders/
       │── __init__.py
       └── books.py

目的のスクレイピングロジックを実装するには、books_scraper/spiders/books.pyの内容を以下のコードに置き換える:

import scrapy

class BooksSpider(scrapy.Spider):
    name = "books"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["https://books.toscrape.com/catalogue/page-1.html"]

    def parse(self, response):
        # Extract book details
        for book in response.css(".product_pod"):
            yield {
                "title": book.css("h3 a::attr(title)").get(),
                "url": response.urljoin(book.css("h3 a::attr(href)").get()),
                "image": response.urljoin(book.css(".thumbnail::attr(src)").get()),
                "rating": book.css(".star-rating::attr(class)").get().split()[-1],
                "price": book.css(".product_price .price_color::text").get(),
                "availability": book.css(".availability::text").get().strip(),
            }

        # Handle pagination
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

ステップ3:スパイダーの起動

books_scraperフォルダで、有効化された仮想環境で、以下のコマンドを実行してScrapyスパイダーを実行し、スクレイピングされたデータをCSVファイルにエクスポートします:

scrapy crawl books -o books.csv

これで、スクレイピングされたデータを含むbooks.csvファイルが、Playwrightスクリプトが生成したものと同じように生成される。これでミッションは完了だ!

Scrapy vs Playwright:どちらを使うべきか?

Playwrightのスクレイピングスクリプトは6つの長いステップを必要としたが、Scrapyは3つで済んだ。Scrapyがウェブスクレイピング用に設計されているのに対し、Playwrightはテストとスクレイピングの両方に使用される一般的なブラウザ自動化ツールなので、これは驚くべきことではない。

特に重要な違いは、ウェブクローリングのロジックにあった。Playwrightはページネーションに手作業とカスタムロジックを必要としたが、Scrapyは数行のコードで処理できる。

要するに、これらのシナリオのいずれかでは、PlaywrightよりもScrapyを選べということだ:

  1. クローリングサポートを組み込んだ大規模なデータ抽出が必要です。
  2. Scrapyは高速な並列リクエストに最適化されているため、パフォーマンスとスピードが優先される。
  3. ページネーション、再試行、さまざまなフォーマットでのデータ抽出、並列スクレイピングを代行してくれるフレームワークを好む。

逆に、ScrapyよりもPlaywrightの方が好きだ:

  1. ブラウザのレンダリングを必要とするJavaScriptを多用するウェブサイトからデータを抽出する必要がある。
  2. 無限スクロールのようなダイナミックなインタラクションが必要だ。
  3. 複雑なウェブスクレイピングのナビゲーションパターンなど)ユーザーのインタラクションをもっとコントロールしたい。

ScrapyとPlaywrightの比較の最終段階として、以下の表をご覧ください:

特徴 スクラップ 劇作家
開発元 ザイト+コミュニティ マイクロソフト+コミュニティ
ギットハブの星 54k+ 69k+
ダウンロード 38万ドル以上、週給 12M+、ウィークリー
プログラミング言語 パイソン Python, JavaScript, TypeScript, C#
主な目標 ウェブスクレイピングとクローリング ブラウザの自動化、テスト、ウェブスクレイピング
JavaScriptレンダリング ❌ (いくつかのプラグインで可能) ✔️
ブラウザ・インタラクション ❌ (いくつかのプラグインで可能) ✔️
オートアームド・クローリング ✔️ 手作業が必要) ❌ (手作業が必要
プロキシ統合 サポート サポート
パラレルリクエスト 効率的で簡単に設定可能 限定的だが可能
データエクスポート CSV、JSON、XMLなど。 カスタムロジックが必要

プレイライトとスクラップ両方の限界

ScrapyとPlaywrightはどちらもウェブスクレイピングのための強力なツールだが、それぞれに一定の制限がある。

例えばScrapyは、レンダリングやデータ取得をJavaScriptに依存しているサイトから動的コンテンツをスクレイピングするのに苦労している。最近のウェブサイトの多くはJavaScriptを必要とするため、Scrapyは一般的なスクレイピング対策の影響を受けやすい。確かに、PlaywrightはJavaScriptを多用するサイトを扱うことができるが、IP禁止などの課題に直面している。

多くのリクエストを行うと、レートリミッターが作動し、リクエストが拒否されたり、IPが禁止されたりすることがあります。それを軽減するために、プロキシサーバーを統合してIPをローテーションさせることができます。

信頼性の高いプロキシサーバーが必要な場合、Bright Dataのプロキシネットワークは、フォーチュン500の企業や世界中の20,000以上の顧客から信頼されています。そのネットワークには以下が含まれます:

CAPTCHAは、ブラウザで動作する自動スクレイピングボットをブロックするように設計されています。これを克服するために、PlaywrightでCAPTCHAを回避するソリューションを検討することができます。

結論

今回のPlaywright vs Scrapyブログポストでは、Webスクレイピングにおける両ライブラリの役割について学びました。データ抽出のための両ライブラリの機能を調べ、実際のページネーションシナリオでのパフォーマンスを比較しました。

Scrapyはウェブサイトのデータ解析やクロールに必要なものをすべて提供してくれるが、Playwrightはユーザー・インタラクションのシミュレーションにより重点を置いている。

また、IPバンやCAPTCHAなどの制限も発見されたことでしょう。幸いなことに、これらの課題はプロキシやBright DataのCAPTCHA Solverのような専用のボット対策ソリューションを使って克服することができます。

Bright Dataの無料アカウントを作成して、プロキシおよびスクレイピングソリューションをお試しください!

クレジットカードは必要ありません