0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS LambdaとDockerで作るWebスクレイピング(Selenium)

Last updated at Posted at 2025-10-05

1. はじめに

Webサイトから定期的に情報を取得したい場面は多くありますが、常時サーバーを稼働させるのはコストや管理の負担が大きな課題です。本記事では、AWS LambdaとDockerを活用し、必要な時だけ動作する効率的なスクレイピング環境を構築する方法を紹介します。Seleniumを用いて動的ページにも対応し、取得結果をメールで通知する仕組みを分かりやすく解説します。

  • 本記事は、Copilot や Claude に質問しながら検証した内容を整理したメモです。どなたかの参考になれば幸いです。

2. 更新情報

  • 2025/10/05
    本記事を公開しました。今後、各ソフトウェアのバージョンが更新され次第、内容も随時アップデートしていきます。

3. 動作環境

4. テスト環境

5. 動作イメージ

このシステムは以下のような流れで動作します。すべての処理は自動で行われ、結果はメールで受信されます。動作が少なければ、ほぼ無料で構築可能です。

ステップ         処理内容
Lambda関数起動 コンテナイメージからLambda環境が立ち上がり、スクレイピング処理を開始(課金開始)
ブラウザ起動 SeleniumまたはPlaywrightがヘッドレスChromeを起動
Webページアクセス 指定されたURLにアクセスし、ページの読み込みを待機
要素の取得 CSSセレクタで指定した要素からデータを抽出
メール送信 取得したデータまたはエラー情報をGmail経由でメール通知
リソース解放 ブラウザを終了し、Lambda関数が停止(課金停止)

<メール受信イメージ>

Webスクレイピングが正常に完了しました。

取得データ: 21.8
対象URL: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
CSSセレクタ: #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp
スクレイピングモード: Selenium
実行時刻: 2025-10-05 19:59:27 JST

6. Webスクレイピングするサイト

気象庁 愛知県 名古屋の観測データ
https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414

  • 名古屋市における最新の気温データ( 下記赤枠のデータ )を取得します

webs-02.png

気象庁ホームページは、スクレイピング自体を全面的に禁止しているわけではありませんが、利用規約や注意事項に従う必要があります。
気象庁ホームページ 利用規約

  • 自動取得(スクレイピング)についての明示的な禁止条項はなし
  • 一般的に、過剰なアクセスやサーバーに負荷をかける行為は禁止されています
  • アクセス頻度が高すぎると、業務妨害やアクセス制限の対象になる可能性があります

7. CSSセレクタについて

Webスクレイピングは、Webページから必要なデータを自動で取得する技術です。しかし、ページ全体から「どこに欲しい情報があるのか」を機械に教えなければ、正しいデータは取れません。その「場所」を指定するために使うのが ロケータ(Locator) です。ロケータにはいくつか種類がありますが、代表的なのが CSSセレクタ と XPath です。

今回はCSSセレクタを利用し、気象庁のホームページから 名古屋の気温データ を取得します。

7-1. CSSセレクタ取得方法

ブラウザ(例: Google Chrome)から「気象庁のホームページ 名古屋市」へアクセスします。最新の気温を取得するため、気温の位置を 右クリック → 検証 を選択します。

webs-01.png

デベロッパーツール起動後、取得したいデータを探し、 右クリック → Copy selector を選択すると、クリップボードにCSSセレクタがコピーされます。

webs-02.png

コピーされた CSSセレクタ は下記の通りです。

webs-03.png

7-2.(まとめ)URL & CSSセレクタ値

項目
URL https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
CSSセレクタ   #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp

8. Python使用ライブラリの選定

使用ライブラリの選定では、対象ページが静的か動的かを判別し、目的・性能・保守性に合うツールを選びます。

ライブラリ 対応ページ   特徴
requests + BeautifulSoup 静的のみ 最もシンプルで初心者向け。学習コストが低く軽量高速。シンプルなデータ取得に最適で、Lambda環境でも軽快に動作します。情報が豊富で困ったときも解決しやすい。
requests + lxml 静的のみ BeautifulSoupより高速なパーサー。XPath対応で複雑な要素選択が可能。大量データ処理に向き、メモリ使用量が少ないためLambda環境に適しています。
Selenium 静的・動的 ブラウザ実操作でJavaScript動的コンテンツに対応。歴史が長く情報豊富。起動が遅くメモリ消費大。ログイン必要サイトや複雑な操作にも対応可能。
Playwright 静的・動的 Microsoft開発の次世代ツール。Seleniumより高速で安定性が高く最新Web技術対応。直感的APIでネットワーク制御など高度機能を搭載。モダンな選択肢。

本記事ではオールマイティに対応できる Selenium を利用してデータを取得します。

9. 事前準備

9-1. AWSアカウント作成

AWSのアカウントが無い場合は、トップページの アカウントの作成 から登録します。アカウント作成は無料です。

AWSホームページ アカウント作成

9-2. AWS CLIインストール

AWS CLI(Command Line Interface)は、AWSのサービスをコマンドラインから操作するための公式ツールです。ターミナルやコマンドプロンプトから、Lambda関数のデプロイなど、AWS Management Consoleで行える操作をコマンドで実行できます。

AWS CLIダウンロードページ

インストール後に Windows PowerShell を起動し、以下のコマンドを実行してください。 バージョン情報が表示されれば、インストールは完了です。

aws --version

PS C:\Users\pen> aws --version
aws-cli/2.31.7 Python/3.13.7 Windows/11 exe/AMD64

9-3. AWS CLI 認証情報登録

AWS CLI(Command Line Interface)の初期設定コマンドで、AWSを操作するための認証情報やデフォルト設定を登録します。

aws configure

PS C:\Users\pen> aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: us-east-1
Default output format [None]: json

AWS Access Key ID(アクセスキー)

  • 「ユーザー名」に相当する公開キー
  • 「どのユーザーがリクエストしているか」を識別するために使われます
  • キーの入手は下記「AWS CLI 認証情報はどこで入手するのか?」をご覧ください

AWS Secret Access Key(シークレットアクセスキー)

  • 「パスワード」に相当する秘密キー
  • Access Key ID とペアで使われ、署名を生成してリクエストの正当性を証明します
  • キーの入手は下記「AWS CLI 認証情報はどこで入手するのか?」をご覧ください

Default region name

  • どのリージョンを使うかを指定
  • us-east-1 本記事では、米国バージニア北部 を利用します

Default output format

  • CLI実行結果のフォーマットを選択
  • json でテキスト出力 (注意: 小文字入力

9-4. AWS CLI 認証情報はどこで入手するのか?

  1. AWSマネジメントコンソールにログイン
  2. 右上のユーザー名 → セキュリティ認証情報 を開く
  3. アクセスキー セクションで新しいキーを作成
    Access Key IDSecret Access Key が発行されます
    Secret Access Keyは 最初に表示されたときしか確認できないので、必ず安全な場所に保存してください

再び、コマンドを入力して確認します。

aws configure

PS C:\Users\pen> aws configure
AWS Access Key ID [****************XXXX]:
AWS Secret Access Key [****************XXXX]:
Default region name [us-east-1]:
Default output format [json]:

9-5. Dockerインストール

Dockerは、アプリケーションと実行環境を「コンテナ」としてパッケージ化する技術です。本システムでは、Dockerがローカル開発環境とAWS Lambda実行環境の完全な統一を実現しています。Dockerfileで定義した環境は、開発者のパソコンでもAWS Lambdaでも全く同じように動作します。Chrome for TestingやChromeDriverなどの依存関係をコンテナイメージに含めることで、「ローカルでは動くのにLambdaでは動かない」という問題を防ぎます。さらに、Amazon ECRにイメージをプッシュするだけでデプロイが完了するため、複雑な設定は不要です。開発・テスト・本番環境の一貫性が保たれ、安定したスクレイピングシステムを構築できます。

Dockerは下記のサイトからダウンロードできます。 特別な設定は不要 で、インストーラを起動するだけで環境が構築されます。

Dockerホームページ(ダウンロード)

9-6. Gmail STMPを利用

気象庁アメダスサイトから名古屋市の現在気温を自動取得し、その結果をGmail SMTP経由でメール通知します。取得に成功した場合は気温データと実行時刻を、エラー発生時には詳細なエラー情報を送信します。

メール送信を行うには、以下の設定が必要です。

9-7. Googleアカウントの2段階認証を有効化

  • 2段階認証プロセスのページへアクセスする
  • Googleアカウントでログインします
  • アプリパスワードを選択
  • アプリ名を入力します
    (何でも構いません 例: AWS Webスクレイピング)
  • 生成ボタンを押します
  • ウィンドウが開き 16桁のパスワード が表示されます

9-8. (まとめ)Gmail SMTP設定値

項目 説明
MAIL_FROM 〇〇〇@gmail.com 送信元Gmailアドレス(Googleアカウント)
MAIL_TO △△△@example.com 送信先メールアドレス
MAIL_PASS xxxxxxxxxxxxxxxx Googleアプリパスワード(16桁)
SMTP_SERVER smtp.gmail.com Gmail SMTPサーバー
SMTP_PORT 587 SMTP通信ポート

9-9. プロジェクトのフォルダ構成

ローカルでDockerによる検証を行います。以下のようにすべてのファイルを同じフォルダに配置してください。本紹介では、下記の場所で aws-lambda-selenium-webscraping フォルダを作成し、各ファイルを格納します。

C:\Users\pen\python\ aws-lambda-selenium-webscraping

9-9-1. フォルダ・ファイル構成

aws-lambda-selenium-webscraping/
├── main.py
├── requirements.txt
├── Dockerfile
├── .env
└── docker-compose.yml

9-9-2. 各ファイルの役割

ファイル名 役割
main.py Seleniumを使ったWebスクレイピングの実装
(Pythonプログラム)
requirements.txt Seleniumライブラリの依存関係
Dockerfile ChromeとChromeDriverをインストールしたLambda互換環境の構築
.env スクレイピング対象のURLやメール設定などの環境変数
docker-compose.yml    Dockerコンテナの起動設定

9-10. main.py

AWS Lambda上でSeleniumを使用してJavaScript実行が必要な動的WebサイトをスクレイピングするPythonプログラム。

主な特徴:

  • AWS Lambda対応
    サーバーレス環境での実行を前提に設計されており、クラウド上で安定稼働可能

  • SeleniumによるWebスクレイピング
    JavaScript実行が必要な動的ページにも対応し、指定したURLとCSSセレクタから要素を抽出

  • 環境変数による柔軟な設定管理
    .env やLambda環境変数を利用し、URLや通知先などを外部から制御可能

  • ログ出力の統一化
    実行状況やエラーを一貫したフォーマットで記録し、運用時のトラブルシューティングを容易にする

  • メール通知機能
    スクレイピング結果やエラーを自動で送信し、監視・運用を効率化

  • Docker対応
    ローカル開発や検証をDocker環境で再現でき、本番環境との整合性を確保

main.py
# 標準ライブラリ
import os, logging, smtplib, argparse, time
from email.mime.text import MIMEText
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any

# Selenium WebDriverライブラリ
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import WebDriverException, TimeoutException


class Config:
    """環境変数から設定を読み込み、値の妥当性を検証するクラス"""
    
    def __init__(self):
        """環境変数から設定値を読み込み"""
        # 対象URL とCSSセレクタ(環境変数から取得、引用符を除去)
        self.URL = os.getenv("URL", "").strip('"\'')
        self.CSS_SELECTOR = os.getenv("CSS_SELECTOR", "").strip('"\'')
        
        # メール設定(特殊文字対応のため引用符を除去)
        self.MAIL_FROM = os.getenv("MAIL_FROM", "").strip('"\'')
        self.MAIL_TO = os.getenv("MAIL_TO", "").strip('"\'')
        self.MAIL_PASS = os.getenv("MAIL_PASS", "").strip('"\'')
        
        # SMTP設定(環境変数から取得、引用符を除去)
        self.SMTP_SERVER = os.getenv("SMTP_SERVER", "").strip('"\'')
        self.SMTP_PORT = int(os.getenv("SMTP_PORT", "0"))
        
        # タイムアウト設定(環境変数から取得)
        self.SELENIUM_TIMEOUT = int(os.getenv("SELENIUM_TIMEOUT", "0"))
        
        # Chromeバイナリのパス(Dockerfileで設定済みの固定パス)
        self.CHROME_BIN = "/opt/chrome/chrome"
        self.CHROMEDRIVER_BIN = "/opt/chromedriver/chromedriver"
        
        self._validate_config()
    
    def _validate_config(self):
        """設定値の妥当性を検証"""
        if not self.URL:
            raise ValueError("URLが設定されていません。.envファイルでURL環境変数を設定してください")
        
        if not self.CSS_SELECTOR:
            raise ValueError("CSS_SELECTORが設定されていません。.envファイルでCSS_SELECTOR環境変数を設定してください")
        
        if not all([self.MAIL_FROM, self.MAIL_TO, self.MAIL_PASS]):
            raise ValueError("MAIL_FROM, MAIL_TO, MAIL_PASSが設定されていません。.envファイルで設定してください")
        
        if not self.SMTP_SERVER:
            raise ValueError("SMTP_SERVERが設定されていません。.envファイルでSMTP_SERVER環境変数を設定してください")
        
        if self.SMTP_PORT <= 0:
            raise ValueError("SMTP_PORTが設定されていません。.envファイルでSMTP_PORT環境変数を設定してください")
        
        if self.SELENIUM_TIMEOUT <= 0:
            raise ValueError("SELENIUM_TIMEOUTが設定されていません。.envファイルでSELENIUM_TIMEOUT環境変数を設定してください")


class Logger:
    """統一されたログ設定を提供するクラス"""
    
    @staticmethod
    def setup_logger(name: str = "scraper") -> logging.Logger:
        """ロガーインスタンスを作成して返却"""
        logger = logging.getLogger(name)
        logger.setLevel(logging.INFO)
        
        if not logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter(
                "%(asctime)s [%(levelname)s] %(message)s",
                datefmt="%Y-%m-%d %H:%M:%S"
            )
            handler.setFormatter(formatter)
            logger.addHandler(handler)
        
        return logger


class SeleniumScraper:
    """Selenium WebDriver を使用したWebスクレイパー(JavaScript実行が必要な動的コンテンツ用)"""
    
    def __init__(self, config: Config, logger: logging.Logger):
        self.config = config
        self.logger = logger
        self.driver: Optional[webdriver.Chrome] = None
    
    def _create_chrome_options(self) -> Options:
        """ヘッドレスブラウジング用のChromeオプションを作成"""
        options = Options()
        options.binary_location = self.config.CHROME_BIN
        
        # 基本的なヘッドレス設定
        chrome_options = [
            "--headless=new",
            "--no-sandbox", 
            "--disable-dev-shm-usage",
            "--disable-gpu",
            "--window-size=800,600",
            
            # Lambda環境でのパフォーマンス最適化
            "--disable-web-security",
            "--disable-features=VizDisplayCompositor",
            "--disable-extensions",
            "--disable-plugins", 
            "--disable-images",
            "--single-process",
            "--no-zygote",
            "--disable-background-timer-throttling",
            "--disable-backgrounding-occluded-windows",
            "--disable-renderer-backgrounding",
            "--disable-background-networking",
            "--disable-ipc-flooding-protection",
            "--memory-pressure-off",
            "--max_old_space_size=4096",
            "--disable-logging",
            "--disable-gl-drawing-for-tests",
            "--disable-software-rasterizer",
            
            # リモートデバッグポート(Lambda通信用)
            "--remote-debugging-port=9222",
            
            # 一時ディレクトリ(/tmp使用)
            "--user-data-dir=/tmp/chrome-user-data",
            "--data-path=/tmp/chrome-data", 
            "--disk-cache-dir=/tmp/chrome-cache"
        ]
        
        for option in chrome_options:
            options.add_argument(option)
        
        return options
    
    def _create_driver(self) -> webdriver.Chrome:
        """Chrome WebDriverインスタンスを作成"""
        options = self._create_chrome_options()
        service = Service(executable_path=self.config.CHROMEDRIVER_BIN)
        
        self.logger.info("Chrome WebDriverを起動中 (chrome=%s, chromedriver=%s)", 
                        self.config.CHROME_BIN, self.config.CHROMEDRIVER_BIN)
        
        try:
            driver = webdriver.Chrome(service=service, options=options)
            driver.set_page_load_timeout(self.config.SELENIUM_TIMEOUT)
            driver.set_script_timeout(self.config.SELENIUM_TIMEOUT)
            return driver
        except WebDriverException as e:
            self.logger.error("Chrome WebDriverの起動に失敗: %s", str(e))
            raise
    
    def scrape(self) -> str:
        """Seleniumを使用してデータを取得"""
        try:
            self.driver = self._create_driver()
            
            self.logger.info("Seleniumでページを開いています: %s", self.config.URL)
            self.driver.get(self.config.URL)
            
            # 要素が表示されるまで待機
            wait = WebDriverWait(self.driver, self.config.SELENIUM_TIMEOUT)
            element = wait.until(
                EC.visibility_of_element_located((By.CSS_SELECTOR, self.config.CSS_SELECTOR))
            )
            
            data = element.text.strip()
            self.logger.info("データ取得成功: %s", data)
            return data
            
        except TimeoutException:
            error_msg = f"要素が見つからないかタイムアウトしました。CSSセレクタ: {self.config.CSS_SELECTOR}"
            self.logger.error(error_msg)
            raise ValueError(error_msg)
        except WebDriverException as e:
            error_msg = f"WebDriverエラー: {str(e)}"
            self.logger.error(error_msg)
            raise ValueError(error_msg)
        except Exception as e:
            error_msg = f"Selenium スクレイピングで予期しないエラー: {str(e)}"
            self.logger.error(error_msg)
            raise ValueError(error_msg)
        finally:
            self._cleanup_driver()
    
    def _cleanup_driver(self):
        """WebDriverリソースをクリーンアップ"""
        if self.driver:
            try:
                self.driver.quit()
                self.logger.info("WebDriverを正常に終了しました")
            except Exception as e:
                self.logger.error("WebDriverの終了に失敗: %s", str(e))
            finally:
                self.driver = None


class EmailSender:
    """通知メール送信クラス"""
    
    def __init__(self, config: Config, logger: logging.Logger):
        self.config = config
        self.logger = logger
        self.jst = timezone(timedelta(hours=9))
    
    def send_success_mail(self, data: str) -> str:
        """成功通知メールを送信"""
        subject = "AWS Lambda Webスクレイピング - 成功"
        body = self._create_success_body(data)
        return self._send_mail(subject, body)
    
    def send_error_mail(self, error_info: str) -> str:
        """エラー通知メールを送信"""
        subject = "AWS Lambda Webスクレイピング - エラー"
        body = self._create_error_body(error_info)
        return self._send_mail(subject, body)
    
    def _create_success_body(self, data: str) -> str:
        """成功メールの本文を作成"""
        timestamp = datetime.now(self.jst).strftime("%Y-%m-%d %H:%M:%S JST")
        return f"""Webスクレイピングが正常に完了しました。

取得データ: {data}
対象URL: {self.config.URL}
CSSセレクタ: {self.config.CSS_SELECTOR}
スクレイピングモード: Selenium
実行時刻: {timestamp}
"""
    
    def _create_error_body(self, error_info: str) -> str:
        """エラーメールの本文を作成"""
        timestamp = datetime.now(self.jst).strftime("%Y-%m-%d %H:%M:%S JST")
        return f"""Webスクレイピングでエラーが発生しました。

エラー詳細: {error_info}
対象URL: {self.config.URL}
CSSセレクタ: {self.config.CSS_SELECTOR}
スクレイピングモード: Selenium
実行時刻: {timestamp}
"""
    
    def _send_mail(self, subject: str, body: str) -> str:
        """SMTP経由でメールを送信"""
        msg = MIMEText(body, "plain", "utf-8")
        msg["Subject"] = subject
        msg["From"] = self.config.MAIL_FROM
        msg["To"] = self.config.MAIL_TO
        
        self.logger.info("メール送信中 %s から %s へ (%s:%s経由)", 
                        self.config.MAIL_FROM, self.config.MAIL_TO, 
                        self.config.SMTP_SERVER, self.config.SMTP_PORT)
        
        try:
            with smtplib.SMTP(self.config.SMTP_SERVER, self.config.SMTP_PORT, timeout=30) as server:
                server.starttls()
                server.login(self.config.MAIL_FROM, self.config.MAIL_PASS)
                server.send_message(msg)
            
            self.logger.info("メール送信成功")
            return "sent"
            
        except Exception as e:
            self.logger.error("メール送信失敗: %s", str(e))
            raise


class WebScrapingService:
    """メインのWebスクレイピングサービス"""
    
    def __init__(self):
        self.config = Config()
        self.logger = Logger.setup_logger()
        self.email_sender = EmailSender(self.config, self.logger)
        
        # 起動時に設定をログ出力
        self._log_configuration()
    
    def _log_configuration(self):
        """現在の設定をログ出力"""
        self.logger.info("=== Webスクレイピングサービス開始 ===")
        self.logger.info("URL: %s", self.config.URL)
        self.logger.info("CSS_SELECTOR: %s", self.config.CSS_SELECTOR)
        self.logger.info("MAIL_FROM: %s", self.config.MAIL_FROM)
        self.logger.info("MAIL_TO: %s", self.config.MAIL_TO)
        self.logger.info("SMTP_SERVER: %s:%s", self.config.SMTP_SERVER, self.config.SMTP_PORT)
        self.logger.info("SELENIUM_TIMEOUT: %s秒", self.config.SELENIUM_TIMEOUT)
    
    def scrape_data(self) -> str:
        """Selenium WebDriverを使用してWebスクレイピングを実行"""
        self.logger.info("Seleniumを使用してスクレイピングを開始")
        scraper = SeleniumScraper(self.config, self.logger)
        return scraper.scrape()
    
    def run(self, send_email: bool = True) -> Dict[str, Any]:
        """完全なスクレイピングと通知プロセスを実行"""
        try:
            # スクレイピングを実行
            data = self.scrape_data()
            
            # 要求された場合は成功通知を送信
            if send_email:
                try:
                    mail_result = self.email_sender.send_success_mail(data)
                    return {
                        "status": "success", 
                        "data": data, 
                        "mail": mail_result
                    }
                except Exception as e:
                    self.logger.error("成功メールの送信に失敗: %s", str(e))
                    return {
                        "status": "partial_success", 
                        "data": data, 
                        "mail_error": str(e)
                    }
            else:
                return {
                    "status": "success",
                    "data": data,
                    "mail": "skipped"
                }
                
        except Exception as e:
            error_info = str(e)
            self.logger.error("スクレイピングに失敗: %s", error_info)
            
            # 要求された場合はエラー通知を送信
            if send_email:
                try:
                    self.email_sender.send_error_mail(error_info)
                    return {
                        "status": "error", 
                        "error": error_info, 
                        "mail": "error_sent"
                    }
                except Exception as mail_e:
                    self.logger.error("エラーメールの送信に失敗: %s", str(mail_e))
                    return {
                        "status": "error", 
                        "error": error_info, 
                        "mail_error": str(mail_e)
                    }
            else:
                return {
                    "status": "error",
                    "error": error_info,
                    "mail": "skipped"
                }


# ====== Lambda ハンドラー ======
def lambda_handler(event, context):
    """AWS Lambda エントリーポイント"""
    # イベントからno_mailパラメータを確認(デフォルトはメール送信有効)
    send_email = not event.get("no_mail", False)
    service = WebScrapingService()
    return service.run(send_email=send_email)


# ====== ローカルテスト用 ======
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Selenium Webスクレイピング")
    parser.add_argument("--no-mail", action="store_true", 
                       help="スクレイピングのみ実行(メール送信なし)")
    
    args = parser.parse_args()
    
    service = WebScrapingService()
    result = service.run(send_email=not args.no_mail)
    
    print(f"\n=== 実行結果 ===")
    print(f"ステータス: {result['status']}")
    if 'data' in result:
        print(f"取得データ: {result['data']}")
    if 'error' in result:
        print(f"エラー: {result['error']}")
    print(f"メール: {result.get('mail', 'N/A')}")

9-11. requirements.txt

AWS Lambda上でのSelenium Webスクレイピングに必要な最小限の依存関係を定義したファイルです。

主な特徴:

  • シンプル構成
    seleniumのバージョンを指定
  • バージョン固定
    予期しない動作変更を防ぐため完全バージョン指定
  • 軽量化
    必要最小限のパッケージでLambdaのデプロイサイズを抑制
  • Selenium
    最新のSeleniumを採用し、WebDriver W3C標準に完全対応

シンプルな依存関係管理により、デプロイの高速化とメンテナンス性向上を実現しています。

requirements.txt
selenium==4.35.0

9-12. Dockerfile

AWS LambdaコンテナイメージでSeleniumを動作させるための設定ファイルです。

主な特徴:

  • Lambda 公式ベースイメージ:
    最新の python を使用
  • Chrome for Testing 採用:
    安定版バージョン固定でインストール
  • バージョン同期:
    Chrome と ChromeDriverを同一バージョンで統一し互換性を確保
  • 依存ライブラリ完備:
    nss、libX11、gtk3等、Chrome 実行に必要な全パッケージを導入
  • 作業ディレクトリ準備:
    /tmp配下にキャッシュ用ディレクトリを事前作成・権限設定
  • 検証機能:
    インストール確認用のechoコマンドでデバッグを容易化
  • 最適化:
    dnf clean all--no-cache-dirでイメージサイズを削減

マルチステージビルド不要のシンプル構成で、Lambda環境でのSelenium実行を実現します。

Dockerfile
# Python バージョン指定
FROM public.ecr.aws/lambda/python:3.13

# Chrome for Testingバージョン指定
ENV CHROME_VERSION=141.0.7390.54
ENV CHROMEDRIVER_VERSION=141.0.7390.54

# 必要なパッケージをまとめてインストール
RUN dnf install -y unzip findutils && dnf clean all

# Chrome for Testing インストール
RUN cd /tmp && \
    curl -f -s -L -O https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/${CHROME_VERSION}/linux64/chrome-linux64.zip && \
    unzip chrome-linux64.zip && \
    ls -la chrome-linux64/ && \
    mv chrome-linux64 /opt/chrome && \
    ls -la /opt/chrome/ && \
    chmod +x /opt/chrome/chrome && \
    rm -f chrome-linux64.zip

# ChromeDriver インストール
RUN cd /tmp && \
    curl -f -s -L -O https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/${CHROMEDRIVER_VERSION}/linux64/chromedriver-linux64.zip && \
    unzip chromedriver-linux64.zip && \
    ls -la chromedriver-linux64/ && \
    mv chromedriver-linux64 /opt/chromedriver && \
    ls -la /opt/chromedriver/ && \
    chmod +x /opt/chromedriver/chromedriver && \
    rm -f chromedriver-linux64.zip

# Chrome 実行に必要な全パッケージ
RUN dnf install -y \
    nss \
    libX11 \
    libxcb \
    dbus-libs \
    gtk3 \
    libXcomposite \
    libXdamage \
    libXext \
    libXfixes \
    libXrandr \
    libXtst \
    libdrm \
    mesa-libgbm \
    alsa-lib \
    && dnf clean all

# Chrome 作業ディレクトリ準備
RUN mkdir -p /tmp/chrome-user-data /tmp/chrome-data /tmp/chrome-cache && \
    chmod -R 777 /tmp/chrome-user-data /tmp/chrome-data /tmp/chrome-cache

# インストール確認
RUN echo "=== Chrome files ===" && ls -la /opt/
RUN echo "=== Testing Chrome ===" && ls -la /opt/chrome/ || echo "Chrome directory not found"
RUN echo "=== Testing ChromeDriver ===" && ls -la /opt/chromedriver/ || echo "ChromeDriver directory not found"

# Python ライブラリ
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# アプリ配置
COPY main.py ${LAMBDA_TASK_ROOT}

# 環境変数
ENV CHROME_BIN=/opt/chrome/chrome
ENV CHROMEDRIVER_BIN=/opt/chromedriver/chromedriver

# Lambda エントリポイント
CMD ["main.lambda_handler"]

9-13. .env

Webスクレイピングアプリケーションの実行に必要な設定を環境変数として管理するファイルです。

主な特徴:

  • 対象サイト設定
    気象庁のアメダスデータページ(名古屋市)をスクレイピング対象に指定
  • 精密なセレクタ
    CSSセレクタで気温データをピンポイントで取得
  • メール通知設定
    Gmail SMTP経由で実行結果を通知
  • アプリパスワード
    MAIL_PASSにGmailアプリパスワードを使用しセキュアに認証
  • SMTP設定
    Gmailの標準設定(smtp.gmail.com:587)でSTARTTLS接続
  • タイムアウト制御
    60秒のタイムアウトで動的コンテンツの読込完了を待機
.env
# データを取得するURL
URL="https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414"

# CSSセレクタ
CSS_SELECTOR="#amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp"

# メール設定
MAIL_TO="△△△@example.com"
MAIL_FROM="〇〇〇@gmail.com"
MAIL_PASS="xxxxxxxxxxxxxxxx"

# SMTP設定
SMTP_SERVER="smtp.gmail.com"
SMTP_PORT=587

# タイムアウト設定
SELENIUM_TIMEOUT=60

9-14. docker-compose.yml

Selenium Webスクレイピングアプリケーションをローカルでテスト実行するための設定ファイルです。

主な特徴:

  • シンプル構成
    単一サービス定義でミニマルな設定
  • 自動ビルド
    カレントディレクトリのDockerfileから自動的にイメージをビルド
  • 環境変数管理
    env_file: .envで設定を外部ファイルから一括読み込み
  • コンテナ名固定
    aws-lambda-selenium-webscrapingで識別を容易化
  • 対話モード
    tty: trueでコンテナを起動状態に保持し、デバッグやログ確認が可能
docker-compose.yml
# Docker Composeファイル形式のバージョン指定
version: "3.9"

# サービス(コンテナ)の定義
services:
  
  # サービスの名前
  selenium-scraper:
    
    # 現在ディレクトリのDockerfileからイメージをビルド
    build: .
    
    # 起動するコンテナの名前
    container_name: aws-lambda-selenium-webscraping
    
    #.envファイルから環境変数を自動読み込み
    env_file: .env
    
    # 擬似端末(pseudo-TTY)を割り当て
    tty: true

10. ローカル環境でのテスト(Docker)

各種ファイルをAWSへアップロードする前に、ローカル環境において動作テストを実施します。

10-1. Docker起動

Dockerを起動します。左下のステータスが Engine running になっていることを確認します。
webs-04.png

10-2. Dockerイメージのビルド

Dockerイメージをビルドします。下記のコマンドを実行します。

<ポイント>
main.py / requirements.txt / Dockerfile / .env / docker-compose.yml ファイルが配置されている場所で実行してください

docker build --platform linux/amd64 --provenance=false -t aws-lambda-selenium-webscraping .

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker build --platform linux/amd64 --provenance=false -t aws-lambda-selenium-webscraping .
[+] Building 226.2s (17/17) FINISHED                                                               docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 2.32kB                                                                             0.0s
 => [internal] load metadata for public.ecr.aws/lambda/python:3.13                                                 2.4s
 => [internal] load .dockerignore                                                                                  0.1s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load build context                                                                                  0.1s
 => => transferring context: 16.27kB                                                                               0.0s
 => [ 1/12] FROM public.ecr.aws/lambda/python:3.13@sha256:3d3dd33b9dc91232af3736475fb593f9e3e51efe614730f7f7658d  24.9s
 => => resolve public.ecr.aws/lambda/python:3.13@sha256:3d3dd33b9dc91232af3736475fb593f9e3e51efe614730f7f7658d702  0.1s
 => => sha256:5a8b1154c19da4acc5197e007528335f6ca4ffb438fd40a441f55e4318fc297f 14.19kB / 14.19kB                   0.2s

~ 省略 ~

 => [11/12] RUN pip install --no-cache-dir -r requirements.txt                                                     5.8s
 => [12/12] COPY main.py /var/task                                                                                 0.1s
 => exporting to image                                                                                            66.0s
 => => exporting layers                                                                                           43.8s
 => => exporting manifest sha256:c68d82c62f8abc6fe97edf31f17ff18b99a28a5bd14cbeb4c87a496a57b950f7                  0.0s
 => => exporting config sha256:761c1f219496e268d24e4fcc7cfed0c09dc105115fd7fae249dbf206a8e18051                    0.0s
 => => naming to docker.io/library/aws-lambda-selenium-webscraping:latest                                          0.0s
 => => unpacking to docker.io/library/aws-lambda-selenium-webscraping:latest                                      22.1s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/56c6o2tn9zm1w1qnu94lb4sco

イメージが作成されたことを確認します。
docker images

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker images
REPOSITORY                        TAG       IMAGE ID       CREATED              SIZE
aws-lambda-selenium-webscraping   latest    c68d82c62f8a   About a minute ago   2.34GB

Amazon Linux であることを確認します。
docker run --rm --entrypoint cat aws-lambda-selenium-webscraping /etc/os-release

  • NAME="Amazon Linux"
  • VERSION="2023"
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --entrypoint cat aws-lambda-selenium-webscraping /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023.8.20250915"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/"
DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/"
SUPPORT_URL="https://aws.amazon.com/premiumsupport/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
VENDOR_NAME="AWS"
VENDOR_URL="https://aws.amazon.com/"
SUPPORT_END="2029-06-30"
VARIANT_ID="202509171338-2023.491.0"

Python のバージョンを確認します。
docker run --rm --entrypoint python aws-lambda-selenium-webscraping --version

  • Python 3.13.7
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --entrypoint python aws-lambda-selenium-webscraping --version
Python 3.13.7

Selenium のバージョンを確認します。
docker run --rm --entrypoint pip aws-lambda-selenium-webscraping list

  • selenium 4.35.0
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --entrypoint pip aws-lambda-selenium-webscraping list
Package             Version
------------------- -----------
attrs               25.3.0
awslambdaric        3.1.1
boto3               1.40.4
botocore            1.40.4
certifi             2025.10.5
h11                 0.16.0
idna                3.10
jmespath            1.0.1
outcome             1.3.0.post0
pip                 25.2
PySocks             1.7.1
python-dateutil     2.9.0.post0
s3transfer          0.13.1
selenium            4.35.0
simplejson          3.20.1
six                 1.17.0
snapshot-restore-py 1.0.0
sniffio             1.3.1
sortedcontainers    2.4.0
trio                0.30.0
trio-websocket      0.12.2
typing_extensions   4.14.1
urllib3             2.5.0
websocket-client    1.8.0
wsproto             1.2.0

Google Chrome for TestingChromeDriver のバージョンを確認します。
docker run --rm --entrypoint /opt/chrome/chrome aws-lambda-selenium-webscraping --version
docker run --rm --entrypoint /opt/chromedriver/chromedriver aws-lambda-selenium-webscraping --version

  • Google Chrome for Testing 141.0.7390.54
  • ChromeDriver 141.0.7390.54
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --entrypoint /opt/chrome/chrome aws-lambda-selenium-webscraping --version
Google Chrome for Testing 141.0.7390.54

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --entrypoint /opt/chromedriver/chromedriver aws-lambda-selenium-webscraping --version
ChromeDriver 141.0.7390.54 (b95610d5c4a562d9cd834bc0a098d3316e2f533f-refs/branch-heads/7390@{#2013})

10-3. Dockerコンテナ内での直接実行テスト(メールなし)

Lambda関数をPythonスクリプトとして直接実行し、スクレイピング処理が正常に動作するか確認します。Lambda環境ではなく通常のPython実行形式のため、デバッグが容易で開発時の動作確認に適しています。--no-mailオプションでメール送信をスキップでき、素早く動作検証が可能です。

ローカル環境でプログラムを実行します。実行後、 取得データの数値気象庁のホームページ 名古屋市(気温)が同じであることを確認します。

docker run --rm --env-file .env --entrypoint python aws-lambda-selenium-webscraping main.py --no-mail

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --env-file .env --entrypoint python aws-lambda-selenium-webscraping main.py --no-mail
2025-10-05 10:03:53 [INFO] === Webスクレイピングサービス開始 ===
2025-10-05 10:03:53 [INFO] URL: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
2025-10-05 10:03:53 [INFO] CSS_SELECTOR: #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp
2025-10-05 10:03:53 [INFO] MAIL_FROM: 〇〇〇@gmail.com
2025-10-05 10:03:53 [INFO] MAIL_TO: △△△@example.com
2025-10-05 10:03:53 [INFO] SMTP_SERVER: smtp.gmail.com:587
2025-10-05 10:03:53 [INFO] SELENIUM_TIMEOUT: 60秒
2025-10-05 10:03:53 [INFO] Seleniumを使用してスクレイピングを開始
2025-10-05 10:03:53 [INFO] Chrome WebDriverを起動中 (chrome=/opt/chrome/chrome, chromedriver=/opt/chromedriver/chromedriver)
2025-10-05 10:03:54 [INFO] Seleniumでページを開いています: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
2025-10-05 10:03:55 [INFO] データ取得成功: 21.8
2025-10-05 10:03:55 [INFO] WebDriverを正常に終了しました

=== 実行結果 ===
ステータス: success
取得データ: 21.8
メール: skipped

10-4. Dockerコンテナ内での直接実行テスト(メールあり)

メール送信機能の有効を確認します。指定のメールへ取得したデータが送信されたことを確認します。

docker run --rm --env-file .env --entrypoint python aws-lambda-selenium-webscraping main.py

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm --env-file .env --entrypoint python aws-lambda-selenium-webscraping main.py
2025-10-05 10:04:37 [INFO] === Webスクレイピングサービス開始 ===
2025-10-05 10:04:37 [INFO] URL: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
2025-10-05 10:04:37 [INFO] CSS_SELECTOR: #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp
2025-10-05 10:04:37 [INFO] MAIL_FROM: 〇〇〇@gmail.com
2025-10-05 10:04:37 [INFO] MAIL_TO: △△△@example.com
2025-10-05 10:04:37 [INFO] SMTP_SERVER: smtp.gmail.com:587
2025-10-05 10:04:37 [INFO] SELENIUM_TIMEOUT: 60秒
2025-10-05 10:04:37 [INFO] Seleniumを使用してスクレイピングを開始
2025-10-05 10:04:37 [INFO] Chrome WebDriverを起動中 (chrome=/opt/chrome/chrome, chromedriver=/opt/chromedriver/chromedriver)
2025-10-05 10:04:37 [INFO] Seleniumでページを開いています: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
2025-10-05 10:04:39 [INFO] データ取得成功: 21.8
2025-10-05 10:04:40 [INFO] WebDriverを正常に終了しました
2025-10-05 10:04:40 [INFO] メール送信中 〇〇〇@gmail.com から △△△@example.com へ (smtp.gmail.com:587経由)

=== 実行結果 ===
ステータス: success
取得データ: 21.8
メール: sent
2025-10-05 10:04:43 [INFO] メール送信成功

10-5. Lambda RIEテスト(メールなし)

Lambda Runtime Interface Emulator (RIE) を使用し、AWS Lambdaの実行環境を完全にシミュレートします。コンテナをHTTPサーバーとして起動し、本番と同じHTTPエンドポイント経由でLambda関数を呼び出します。本番デプロイ前の最終確認に最適で、実際のLambda動作を正確に再現できます。

まずは メールなし パターンです。Lambdaコンテナイメージを起動して、HTTPサーバーとして待機させます。

docker run --rm -p 9000:8080 --env-file .env aws-lambda-selenium-webscraping:latest
 ※プログラムを終了する場合は、ctrl + c です。

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker run --rm -p 9000:8080 --env-file .env aws-lambda-selenium-webscraping:latest
05 Oct 2025 10:23:07,433 [INFO] (rapid) exec '/var/runtime/bootstrap' (cwd=/var/task, handler=)

<ポイント>
もう1つ、別の Windows PowerShell を起動します。ローカルで起動中のLambda関数をHTTP経由で呼び出して実行します。

実行後、 取得データの数値気象庁のホームページ 名古屋市(気温)が同じであることを確認します。

Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method POST -Body '{"no_mail": true}' -ContentType "application/json"

PS C:\Users\pen> Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method POST -Body '{"no_mail": true}' -ContentType "application/json"


StatusCode        : 200
StatusDescription : OK
Content           : {"status": "success", "data": "21.8", "mail": "skipped"}
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 56
                    Content-Type: text/plain; charset=utf-8
                    Date: Sun, 05 Oct 2025 10:24:24 GMT

                    {"status": "success", "data": "21.8", "mail": "skipped"}
Forms             : {}
Headers           : {[Content-Length, 56], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 05 Oct 2025 10:24:24
                    GMT]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 56

10-6. Lambda RIEテスト(メールあり)

同様にメールありバージョンをテストします。メールが正常に受信できれば成功です。

Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method POST -Body '{}' -ContentType "application/json"

PS C:\Users\pen> Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method POST -Body '{}' -ContentType "application/json"


StatusCode        : 200
StatusDescription : OK
Content           : {"status": "success", "data": "21.8", "mail": "sent"}
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 53
                    Content-Type: text/plain; charset=utf-8
                    Date: Sun, 05 Oct 2025 10:25:37 GMT

                    {"status": "success", "data": "21.8", "mail": "sent"}
Forms             : {}
Headers           : {[Content-Length, 53], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 05 Oct 2025 10:25:37
                    GMT]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 53

11. AWS ECRへプッシュ

DockerイメージをAWS ECR(Elastic Container Registry)にプッシュします。

11-1. AWS ECR認証

AWS ECRから認証トークンを取得し、Dockerクライアントでログインしています。 Login Succeeded が表示されたことを確認します。

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com

  • us-east-1リージョンのECRに接続
  • XXXXXXXXXXXXは アカウントID(12桁の数値) に置き換えてください
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com
Login Succeeded

11-2. ECRリポジトリの作成

AWS ECRに新しいリポジトリを作成します。ECRはDockerイメージを保存・管理するAWSのコンテナレジストリサービスです。このリポジトリには、Seleniumを使ったWebスクレイピング用のLambda関数のDockerイメージを格納します。

※ リポジトリとは、データやファイルを保管・管理するための保管庫。この後、Dockerイメージをアップロードして、作成したリポジトリに保存します。

aws ecr create-repository --repository-name aws-lambda-selenium-webscraping --region us-east-1

  • aws-lambda-selenium-webscrapingという名前のECRリポジトリを作成
  • すでに存在する場合はエラーになります
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> aws ecr create-repository --repository-name aws-lambda-selenium-webscraping --region us-east-1
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-east-1:XXXXXXXXXXXX:repository/aws-lambda-selenium-webscraping",
        "registryId": "XXXXXXXXXXXX",
        "repositoryName": "aws-lambda-selenium-webscraping",
        "repositoryUri": "XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping",
        "createdAt": "2025-10-05T19:42:40.884000+09:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

11-3. Dockerイメージのタグ付け

このコマンドは、ローカルに存在するDockerイメージに新しいタグを付与する操作です。タグを付けることで、DockerはどのECRリポジトリへプッシュすべきかを認識できるようになります。なお、この処理でイメージ自体がコピーされるわけではなく、既存のイメージに別名(エイリアス)が追加されるだけです。

docker tag aws-lambda-selenium-webscraping:latest XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping:latest

  • ローカルのDockerイメージ aws-lambda-selenium-webscraping:latest に、ECRのフルパスを含む新しいタグを付けています
  • これによりECRにプッシュできる形式になります
  • XXXXXXXXXXXX はアカウントID(12桁の数値) に置き換えてください
PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker tag aws-lambda-selenium-webscraping:latest XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping:latest

docker imagesコマンドを実行すると、先ほどタグ付けしたイメージが一覧に表示されます。ここで注意したいのは、タグを追加しただけでイメージのファイルサイズが増えたわけではないという点です。実際には、同じ IMAGE IDを持つイメージが複数表示されているだけで、実体はひとつのイメージを共有しています。

docker images

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker images
REPOSITORY                                                                     TAG       IMAGE ID       CREATED          SIZE
XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping   latest    c68d82c62f8a   50 minutes ago   2.34GB
aws-lambda-selenium-webscraping                                                latest    c68d82c62f8a   50 minutes ago

11-4. ECRへアップロード

タグ付けしたDockerイメージをアップロードします。

docker push XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping:latest

PS C:\Users\pen\python\aws-lambda-selenium-webscraping> docker push XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping:latest
The push refers to repository [XXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/aws-lambda-selenium-webscraping]
934dfb713ed7: Pushed
18cfd29e41eb: Pushed
204a353b3992: Pushed

~省略~

d5ae151aada7: Pushed
a3153ff6947d: Pushed
27a84ff0acfd: Pushed
183116366c7c: Pushed
latest: digest: sha256:c68d82c62f8abc6fe97edf31f17ff18b99a28a5bd14cbeb4c87a496a57b950f7 size: 3645

12. AWSでの設定

DockerイメージをECRにアップロードした後は、ECRの確認とLambda関数作成、動作確認を行います。

12-1. ECR確認

AWSコンソールでECRへアクセスします。

  1. AWSマネジメントコンソールにログイン
  2. 画面上部の検索バーに ECR または Elastic Container Registry と入力
  3. 検索結果から Elastic Container Registry をクリック

アップロードしたイメージを確認してクリック
webs-06.png

イメージファイルが存在していることを確認
webs-07.png

12-2. Lambda設定

AWSコンソールでLambdaへアクセスします。

  1. AWSマネジメントコンソールにログイン
  2. 画面上部の検索バーに Lambda と入力
  3. 検索結果から Lambda をクリック

コンテナイメージ を選択後、 関数名 を入力します。 イメージを参照 ボタンを押し、アップロードしたファイルを選択します。最後に 関数の作成ボタン を押します。

  • 関数名: aws-lambda-selenium-webscraping

webs-08.png

続いて、メモリ、タイムアウト等を設定します。 設定 タブを押します。
webs-09.png

編集 ボタンを押します。
webs-10.png

メモリエフェメラルストレージタイムアウト に適切な数値を入力します。最後に、 保存 ボタンを押します。

  • メモリ: 1024
  • エフェメラルストレージ: 1024
  • タイムアウト: 1分30秒

webs-11.png

環境変数を設定します。 環境変数 を選択してください。
webs-12.png

環境変数の追加 を押してください。
webs-13.png

環境変数を追加します。.env ファイルと同等の内容を入力してください。設定後、 保存 ボタンを押してください。
webs-14.png

項目
URL https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
CSS_SELECTOR     #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp
MAIL_TO △△△@example.com
MAIL_FROM 〇〇〇@gmail.com
MAIL_PASS xxxxxxxxxxxxxxxx
SMTP_SERVER smtp.gmail.com
SMTP_PORT 587
SELENIUM_TIMEOUT 60

テストを設定します。 テスト タブを選択後、 イベント名イベント JSON を入力します。設定後、保存 ボタンを押します。テスト ボタンを押すと、プログラムが実行されます。

  • イベント名: aws-lambda-selenium-test
  • イベント JSON: {}

webs-15.png

テスト実行後、成功 と表示されることを確認します。
webs-16.png

下記のメールが受信できれば正常に動作しています。

メール内容例

Webスクレイピングが正常に完了しました。

取得データ: 21.8
対象URL: https://www.jma.go.jp/bosai/amedas/#area_type=offices&area_code=230000&amdno=51106&format=table1h&elems=53414
CSSセレクタ: #amd-table > div > div:nth-child(2) > div > div.contents-wide-table-scroll > table > tr:nth-child(4) > td.td-temp
スクレイピングモード: Selenium
実行時刻: 2025-10-05 19:59:27 JST

12-3. AWS Lambda Webスクレイピング 設定完了

以上で、AWS Lambda Webスクレイピング(Selenium)は設定完了です。定期的に実行したい場合は、 Amazon EventBridge を利用します。

13. さいごに

本記事では、AWS LambdaとDockerを組み合わせ、Seleniumを用いた動的Webスクレイピング環境の構築方法を解説しました。Lambdaを利用することで常時稼働サーバーが不要となり、必要な時だけ処理を実行できるため、コストを最小限に抑えつつ安定した運用が可能です。さらに、Dockerによってローカル環境と本番環境の差異を解消し、再現性の高い開発フローを実現しました。取得したデータはGmail SMTPを通じて自動通知されるため、監視や情報収集の効率化にもつながります。環境変数による柔軟な設定管理やログ出力の統一化により、保守性や拡張性も確保しました。少しでも参考になれば幸いです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?