👏

SeleniumとPlaywrightのパフォーマンスを簡単に比べてみる

に公開

TRUSTART株式会社でエンジニアとして働いているnagashunです!

最近、ブラウザ自動化ツールを使った開発でSeleniumからPlaywrightに移行する案件が増えているみたいですが、実際のパフォーマンスの違いってどれくらいあるんでしょうか?

今回は実際にコードを書いて、SeleniumとPlaywrightのパフォーマンスを比較してみたいと思います!

SeleniumとPlaywrightの基本的な違い

Selenium

  • リリース: 2004年から
  • 特徴: 長い歴史があり、多くの言語をサポート
  • アーキテクチャ: WebDriverプロトコル経由でブラウザを制御
  • ブラウザサポート: Chrome、Firefox、Safari、Edge

Playwright

  • リリース: 2020年(Microsoft製)
  • 特徴: モダンなAPI設計、非同期処理に最適化
  • アーキテクチャ: ブラウザと直接通信
  • ブラウザサポート: Chromium、Firefox、WebKit

比較する項目

今回は以下の項目でパフォーマンスを比較します:

  1. 起動時間: ブラウザの起動にかかる時間
  2. ページ読み込み: 特定のページを開くまでの時間
  3. 要素検索: DOMの要素を見つけるまでの時間
  4. クリック操作: ボタンクリックなどの操作速度
  5. メモリ使用量: 実行中のメモリ消費量

環境構築

必要なライブラリをインストールします。

# Selenium
poetry add selenium

# Playwright
poetry add playwright
# Playwrightのブラウザバイナリ(Chromium、Firefox、WebKit)をダウンロード
playwright install

Playwrightが独自のブラウザバイナリを使用する理由:

  • 完全なバージョン制御: システムにインストールされたブラウザのバージョン差による動作の違いを排除
  • テスト環境の統一: 拡張機能、ユーザー設定、キャッシュなどの外部要因を完全に遮断
  • 再現性の保証: ローカル、CI/CD、本番環境で全く同じブラウザ環境を実現
  • 最適化されたパフォーマンス: テスト実行に特化した軽量化とチューニング

パフォーマンス測定コード

Seleniumのテストコード

import time
import psutil
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class SeleniumPerformanceTest:
    def __init__(self):
        self.driver = None
        self.process = psutil.Process(os.getpid())
    
    def setup_browser(self):
        """ブラウザの起動時間を測定"""
        start_time = time.time()
        
        options = Options()
        options.add_argument('--headless')  # ヘッドレスモード
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        
        self.driver = webdriver.Chrome(options=options)
        
        setup_time = time.time() - start_time
        return setup_time
    
    def load_page(self, url):
        """ページ読み込み時間を測定"""
        start_time = time.time()
        self.driver.get(url)
        load_time = time.time() - start_time
        return load_time
    
    def find_element_performance(self, selector):
        """要素検索時間を測定"""
        start_time = time.time()
        element = self.driver.find_element(By.CSS_SELECTOR, selector)
        find_time = time.time() - start_time
        return find_time, element
    
    def click_performance(self, element):
        """クリック操作時間を測定"""
        start_time = time.time()
        element.click()
        click_time = time.time() - start_time
        return click_time
    
    def get_memory_usage(self):
        """メモリ使用量を取得"""
        memory_info = self.process.memory_info()
        return memory_info.rss / 1024 / 1024  # MB単位
    
    def cleanup(self):
        """リソースのクリーンアップ"""
        if self.driver:
            self.driver.quit()

# 実行例
def run_selenium_test():
    test = SeleniumPerformanceTest()
    
    # 起動時間測定
    setup_time = test.setup_browser()
    print(f"Selenium起動時間: {setup_time:.2f}秒")
    
    # ページ読み込み測定
    load_time = test.load_page("https://example.com")
    print(f"ページ読み込み時間: {load_time:.2f}秒")
    
    # 要素検索測定
    find_time, element = test.find_element_performance("h1")
    print(f"要素検索時間: {find_time:.2f}秒")
    
    # メモリ使用量
    memory = test.get_memory_usage()
    print(f"メモリ使用量: {memory:.2f}MB")
    
    test.cleanup()
    return {
        'setup_time': setup_time,
        'load_time': load_time,
        'find_time': find_time,
        'memory': memory
    }

Playwrightのテストコード

import time
import asyncio
import psutil
import os
from playwright.async_api import async_playwright

class PlaywrightPerformanceTest:
    def __init__(self):
        self.browser = None
        self.page = None
        self.process = psutil.Process(os.getpid())
    
    async def setup_browser(self):
        """ブラウザの起動時間を測定"""
        start_time = time.time()
        
        self.playwright = await async_playwright().start()
        self.browser = await self.playwright.chromium.launch(
            headless=True,
            args=['--no-sandbox', '--disable-dev-shm-usage']
        )
        self.page = await self.browser.new_page()
        
        setup_time = time.time() - start_time
        return setup_time
    
    async def load_page(self, url):
        """ページ読み込み時間を測定"""
        start_time = time.time()
        await self.page.goto(url)
        load_time = time.time() - start_time
        return load_time
    
    async def find_element_performance(self, selector):
        """要素検索時間を測定"""
        start_time = time.time()
        element = await self.page.query_selector(selector)
        find_time = time.time() - start_time
        return find_time, element
    
    async def click_performance(self, element):
        """クリック操作時間を測定"""
        start_time = time.time()
        await element.click()
        click_time = time.time() - start_time
        return click_time
    
    def get_memory_usage(self):
        """メモリ使用量を取得"""
        memory_info = self.process.memory_info()
        return memory_info.rss / 1024 / 1024  # MB単位
    
    async def cleanup(self):
        """リソースのクリーンアップ"""
        if self.browser:
            await self.browser.close()
        if hasattr(self, 'playwright'):
            await self.playwright.stop()

# 実行例
async def run_playwright_test():
    test = PlaywrightPerformanceTest()
    
    # 起動時間測定
    setup_time = await test.setup_browser()
    print(f"Playwright起動時間: {setup_time:.2f}秒")
    
    # ページ読み込み測定
    load_time = await test.load_page("https://example.com")
    print(f"ページ読み込み時間: {load_time:.2f}秒")
    
    # 要素検索測定
    find_time, element = await test.find_element_performance("h1")
    print(f"要素検索時間: {find_time:.2f}秒")
    
    # メモリ使用量
    memory = test.get_memory_usage()
    print(f"メモリ使用量: {memory:.2f}MB")
    
    await test.cleanup()
    return {
        'setup_time': setup_time,
        'load_time': load_time,
        'find_time': find_time,
        'memory': memory
    }

# 非同期実行
def run_playwright_sync():
    return asyncio.run(run_playwright_test())

比較実行とベンチマーク

import statistics
import matplotlib.pyplot as plt

def run_benchmark(iterations=5):
    """両方のツールを複数回実行して平均を取る"""
    
    selenium_results = []
    playwright_results = []
    
    print("=== ベンチマーク開始 ===")
    
    # Seleniumのテスト
    print("Seleniumテスト実行中...")
    for i in range(iterations):
        print(f"  実行 {i+1}/{iterations}")
        result = run_selenium_test()
        selenium_results.append(result)
        time.sleep(2)  # 間隔を空ける
    
    # Playwrightのテスト
    print("Playwrightテスト実行中...")
    for i in range(iterations):
        print(f"  実行 {i+1}/{iterations}")
        result = run_playwright_sync()
        playwright_results.append(result)
        time.sleep(2)  # 間隔を空ける
    
    return selenium_results, playwright_results

def analyze_results(selenium_results, playwright_results):
    """結果を分析して表示"""
    
    metrics = ['setup_time', 'load_time', 'find_time', 'memory']
    selenium_avg = {}
    playwright_avg = {}
    
    for metric in metrics:
        selenium_values = [r[metric] for r in selenium_results]
        playwright_values = [r[metric] for r in playwright_results]
        
        selenium_avg[metric] = statistics.mean(selenium_values)
        playwright_avg[metric] = statistics.mean(playwright_values)
    
    # 結果表示
    print("\n=== ベンチマーク結果 ===")
    print(f"起動時間    - Selenium: {selenium_avg['setup_time']:.2f}秒, Playwright: {playwright_avg['setup_time']:.2f}秒")
    print(f"読み込み時間 - Selenium: {selenium_avg['load_time']:.2f}秒, Playwright: {playwright_avg['load_time']:.2f}秒")
    print(f"要素検索時間 - Selenium: {selenium_avg['find_time']:.2f}秒, Playwright: {playwright_avg['find_time']:.2f}秒")
    print(f"メモリ使用量 - Selenium: {selenium_avg['memory']:.2f}MB, Playwright: {playwright_avg['memory']:.2f}MB")
    
    # 改善率の計算
    print("\n=== Playwright vs Selenium 改善率 ===")
    for metric in metrics:
        improvement = (selenium_avg[metric] - playwright_avg[metric]) / selenium_avg[metric] * 100
        print(f"{metric}: {improvement:.1f}% 改善")
    
    return selenium_avg, playwright_avg

# 実行
if __name__ == "__main__":
    selenium_results, playwright_results = run_benchmark(3)
    selenium_avg, playwright_avg = analyze_results(selenium_results, playwright_results)

実測結果と分析

私の環境(MacBook Pro M1、16GB RAM)で実際に測定した結果になります!

測定結果

項目 Selenium Playwright 改善率
起動時間 1.415秒 0.894秒 36.9%
ページ読み込み 0.577秒 0.461秒 20.1%
要素検索 0.005秒 0.023秒 -392.7%
メモリ使用量 41.85MB 41.08MB 1.8%

※各ツールで3回測定した平均値

分析結果

1. 起動時間 - Playwrightが37%高速

初回起動時はSeleniumが3.36秒、Playwrightが2.17秒と大きな差が出ましたが、2回目以降は両者とも大幅に短縮されました。平均ではPlaywrightが約37%高速という結果になりました。

2. ページ読み込み速度 - Playwrightが20%高速

example.comという軽量なページでの測定でしたが、Playwrightの方が一貫して高速でした。

これはCDP(Chrome DevTools Protocol)による効率的な通信が要因と考えられます。CDPはChromeの開発者ツールが使用するプロトコルで、ブラウザと直接やり取りできるため、WebDriverプロトコルを経由するSeleniumよりもオーバーヘッドが少なくなります。

3. 要素検索 - シンプルな検索のためか?Seleniumが高速

Seleniumの要素検索が0.005秒に対し、Playwrightは0.023秒と、Seleniumの方が約5倍高速という結果になりました。h1を探すという簡単な検索くらいでは、逆にSeleniumの方がパフォーマンスが良いこともあるみたいです。

4. メモリ使用量 - ほぼ同等

両者のメモリ使用量は41MB程度とほぼ同等でした。実際に画面に描画をするGUIモードや重いサイトであれば、もう少し差が出たかもしれません。

測定の注意点

  • 環境差: M1 Macでの結果のため、Intel環境では異なる可能性
  • ヘッドレスモード: GUI表示なしでの測定
  • 軽量サイト: example.comという単純なサイトでの測定
  • 初回起動: ブラウザバイナリの初回起動時間が含まれる

まとめ

今回は簡単なサイトでの簡単な操作でしか、測定しなかったのですが、より複雑な操作ではPlaywrightの方が非同期のオーバーヘッドを含めても高速で動いてくれるのかは気になりました!

また実際に実務や個人開発などでも実践的な使い方をした時にどう動くかを見てみたいと思います!

最後に

TRUSTART株式会社は、一緒に働くメンバーを募集しています!
インターンメンバーも大募集中です!
興味を持っていただいた方は、ぜひ弊社のページをご確認ください!!!

https://www.trustart.co.jp/recruit/

TRUSTARTテックブログ

Discussion