AWS Lambdaを介してSeleniumを実行するAPIを作成する

AWS Lambdaを介してSeleniumを実行するAPIを作成してみます。(Windows10)

■PC環境

>Windows 10

>sam –version
SAM CLI, version 1.52.0

>npm –version
8.3.1

>node –version
v16.14.0

>nodeserverless –version

Framework Core: 3.19.0
Plugin: 6.2.2
SDK: 4.3.2

>aws –version
aws-cli/2.5.6 Python/3.9.11 Windows/10 exe/AMD64 prompt/off

>Python バージョン
Python 3.6.8

■Selenium Lambdaレイヤーを作成する

AWSLambdaを介してSeleniumを実行するAPIの作成を行いますが、その前にSelenium Lambdaレイヤーを作成します。作成のためにコマンドプロンプトを起動します。

>pip3.6 install -t selenium/python/lib/python3.6/site-packages selenium==3.8.0

起動後、上記のコマンドを入力し、Enterキーを押します。pipを経由してseleniumバージョン3.8.0をインストールします。「-t」オプションで、インストールされるディレクトリを「selenium/python/lib/python3.6/site-packages」に指定します。

WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting selenium==3.8.0
  Using cached selenium-3.8.0-py2.py3-none-any.whl (941 kB)
Installing collected packages: selenium
Successfully installed selenium-3.8.0
WARNING: Target directory C:\Users\user_\selenium\python\lib\python3.6\site-packages\selenium already exists. Specify --upgrade to force replacement.
WARNING: You are using pip version 21.1.2; however, version 21.3.1 is available.
You should consider upgrading via the 'c:\program files\python36\python.exe -m pip install --upgrade pip' command.

Enterキーを押すと、インストールが開始されます。「Successfully installed」が表示されれば、正常にインストールが完了したことになります。今回はWARNING(警告)が表示されていますが、エラーではないので一旦、無視します。

作業ディレクトリ(カレントディレクトリ)内に「selenium」というディレクトリが作成されます。

コマンドプロンプトを起動します。

>cd selenium

起動後、上記のコマンドを入力し、Enterキーを押します。cdコマンドでseleniumディレクトリに移動します。

zip -r python.zip python/

移動後、上記のコマンドを入力し、Enterキーを押します。Enterキーを押すと、「’zip’ は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。」と出力される場合は、zip.exeのダウンロード(http://gnuwin32.sourceforge.net/packages/zip.htm)を行う必要がある。Binariesのzipファイルをダウンロードし任意のフォルダに解凍。解凍後、「システム環境変数の編集」から環境変数の編集を行います。zipコマンドを用いてファイルを圧縮しpython.zipというzipファイルにします。

Enterキーを押すと、「selenium」に「python.zip」というファイルが生成されます。

生成後「AWS マネジメントコンソール」にログインし、「Lambda」サービスから左メニューの「レイヤー」をクリックします。クリック後、「レイヤーの作成」ボタンをクリックします。

クリックすると「レイヤー設定」が表示されますので、今回は「名前」を「selenium」にし、「説明 – オプション」を「Selenium layer」と入力します。入力後「.zip ファイルをアップロード」に選択し、「アップロード」ボタンをクリックします。

クリックすると、「開く」ウインドウが表示されますので、先程圧縮したzipファイル(python.zip)を選択し、「開く」ボタンをクリックします。

クリックすると、「アップロード」ボタンの右側に選択したzipファイルが表示されます。この状態で次の設定に移ります。「互換性のあるアーキテクチャ – オプション」とありますが、今回これは使用しませんので、チェックはなしで、「互換性のあるランタイム – オプション」で「ランタイム」と表示された入力欄をクリックします。

クリックするとメニューが表示されますので「Python 3.6」を選択します。

選択後、「ライセンス – オプション」は使用しませんので、未入力で「作成」ボタンをクリックします。

クリックすると、「レイヤー ***** のバージョン * が正常に作成されました。」と表示されますので、これが表示されればレイヤーの作成は完了となります。

■Chromedriver Lambdaレイヤーを作成する

今後は、Chromedriver Lambdaレイヤーを作成します。作成のために、Chromeドライバーをダウンロードしますので、コマンドプロンプト上に戻ります。

>mkdir -p chromedriver

戻った後に、上記のコマンドを入力し、Enterキーを押します。「selenium」内に「chromedriver」というディレクトリを作成します。

>cd chromedriver

作成後、上記のコマンドを入力し、Enterキーを押します。cdコマンドでchromedriverディレクトリに移動します。

>curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip

作成後、上記のコマンドを入力し、Enterキーを押します。curlコマンドでchromedriverをダウンロードし、それをchromedriver.zipにします。

Enterキーを押すと、「%」が「100」となり、ダウンロードが完了します。

完了後、「chromedriver」内に「chromedriver.zip」が生成されました。

>unzip chromedriver.zip

生成後、コマンドプロンプトで上記のコマンドを入力し、Enterキーを押します。unzipコマンドで、zip形式で圧縮されたアーカイブを展開します。なお「’unzip.exe’は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」と出力される場合は、unzip.exeをダウンロード(http://gnuwin32.sourceforge.net/packages/unzip.htm)し、ファイルを解凍。解凍後、「システム環境変数の編集」から環境変数の編集を行う必要があります。

Archive:  chromedriver.zip
  inflating: chromedriver

Enterキーを押すと、上記のように出力され、展開が完了します。展開後、chromedriver.zipは削除します。

>curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-41/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip

これでChromeドライバーのダウンロードは完了ですが、次にChromeバイナリをダウンロードする必要がありますので、コマンドプロンプトで上記のコマンドを入力し、Enterキーを押します。curlコマンドでChromeバイナリをダウンロードし、それをheadless-chromium.zipにします。

Enterキーを押すと、「%」が「100」となり、ダウンロードが完了します。

完了後、「chromedriver」内に「headless-chromium.zip」が生成されました。

>unzip headless-chromium.zip

生成後、コマンドプロンプトで上記のコマンドを入力し、Enterキーを押します。unzipコマンドで、zip形式で圧縮されたアーカイブを展開します。

Archive:  headless-chromium.zip
  inflating: headless-chromium

Enterキーを押すと、上記のように出力され、展開が完了します。展開後、headless-chromium.zipは削除します。

>zip -r chromedriver.zip chromedriver headless-chromium

削除後、上記のコマンドを入力し、Enterキーを押します。zipコマンドを用いてchromedriverとheadless-chromiumを圧縮しchromedriver.zipというzipファイルにします。

Enterキーを押すと、「chromedriver」内に「chromedriver.zip」が生成されました。

生成後、Chromedriver Lambdaレイヤーを作成するために、「AWS マネジメントコンソール」にログインし、「Lambda」サービスから左メニューの「レイヤー」をクリックします。クリック後、「レイヤーの作成」ボタンをクリックします。

クリックすると、「レイヤー設定」が表示されますので、今回は「名前」を「chromedriver」とし、「説明 – オプション」を「chrome driver and binary layer」と入力します。入力後「.zip ファイルをアップロード」が選択された状態となっていますが、今回アップロードするchromedriver.zipが10 MBより大きいファイルとなるので、このような場合は、アップロードが遅くなる可能性があるので、「Amazon S3 からファイルをアップロードする」を選択します。

選択後、今回「Lambda」サービスを利用していますが、このサービスがどこの場所であるかを確認します。今回は「米国東部(バージニア北部)「us-east-1」」となっています。

確認後、「S3」サービスへ移動し、今回は「バケット」から「バケットを作成」ボタンをクリックし、新しいバケットを作成します。作成する際に、「AWS リージョン」を、「Lambda」サービスの「米国東部(バージニア北部)「us-east-1」」と同じに設定します。この設定を行わないと、ファイルがアップロードできません。「Error occurred while GetObject. S3 Error Code: PermanentRedirect. S3 Error Message: The bucket is in this region: us-west-2. Please use this region to retry the request」といったエラーが発生します。

S3バケットを作成後、「オブジェクト」から「アップロード」ボタンをクリックします。

クリックすると、「アップロード」が表示されますので、点線部分にファイルをドラッグアンドドロップするか、「ファイルを追加」ボタンで、「chromedriver」内に「chromedriver.zip」を追加します。

今回は「ファイルを追加」ボタンで、「chromedriver」内に「chromedriver.zip」を追加し、「アップロード」ボタンをクリックします。

クリックすると、アップロードが開始されます。

開始後、しばらくすると、アップロードが完了します。完了後、アップロード画面の右側の「閉じる」ボタンをクリックして、画面を閉じます。

閉じると、「オブジェクト」にアップロードしたファイルが追加されますので、このファイルの名前をクリックします。

クリックすると、「オブジェクトの概要」が表示されますので、「オブジェクト URL」をコピーします。

コピー後、「Lambda」の「レイヤー」の「レイヤー設定」に戻り、「Amazon S3 からファイルをアップロードする」を選択し、「Amazon S3 のリンク URL」の入力欄に、URLを貼り付けます。貼り付けた後に、「互換性のあるアーキテクチャ – オプション」は選択せずに、「互換性のあるランタイム – オプション」で入力欄の「ランタイム」をクリックし、「Python 3.6」を選択します。

選択後「ライセンス – オプション」は未入力のまま、「作成」ボタンをクリックします。

クリックすると、「レイヤー ***** のバージョン * が正常に作成されました。」と表示されます。これでChromedriver Lambdaレイヤーの完成となります。

■Lambda関数を作成する

完了後、Lambda関数を作成しますが、その前に「selenium」内に「lambda」ディレクトリを作成します。

作成後、「lambda」ディレクトリに「handler.py」というファイルを作成します。これはLambda関数がかかれたものになります。このファイルをコードエディタで開き、Lambda関数を作成します。今回、コードはこちらのものを参考にさせていただきました(https://dev.to/awscommunity-asean/creating-an-api-that-runs-selenium-via-aws-lambda-3ck3)。

■コード

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def main(event, context):
    options = Options()
    options.binary_location = '/opt/headless-chromium'
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument('--single-process')
    options.add_argument('--disable-dev-shm-usage')

    driver = webdriver.Chrome('/opt/chromedriver',chrome_options=options)
    driver.get('https://laboratory.kazuuu.net/')
    body = f"Headless Chrome Initialized, Page title: {driver.title}"
    driver.close();
    driver.quit();

    response = {
        "statusCode": 200,
        "body":body
    }

    return response

今回はdriver.get()を用い、括弧内に引数,パラメータとして当サイト(https://laboratory.kazuuu.net/)のURLを渡します。これでURLを開き、driver.titleでWebサイトのタイトルを取得。取得後、body変数に格納し、さらにresponse変数にbody変数を格納。最後にreturnでresponse変数の情報を返すというものです。

コードを記述後、保存します。

保存後、「lambda」ディレクトリに「serverless.yaml」というファイルを作成します。このファイルにサーバーレスなアプリケーションを簡単に開発、デプロイするためのツールである「Serverless Framework」の設定を記述します。記述のために、コードエディタで開きます。

今回、コードはこちらのものを参考にさせていただきました(https://dev.to/awscommunity-asean/creating-an-api-that-runs-selenium-via-aws-lambda-3ck3)。

■コード(修正前)

service: selenium-lambda

provider:
  name: aws
  runtime: python3.6
  region: ap-southeast-2
  timeout: 900

functions:
  main:
    memorySize: 1000
    handler: handler.main
    events:
      - http:
          path: test
          method: get

    layers:
      - arn:aws:lambda:ap-southeast-2:{}:layer:chromedriver:2
      - arn:aws:lambda:ap-southeast-2:{}:layer:selenium:2

resources:
  Resources:
    ApiGatewayRestApi:
      Properties:
        BinaryMediaTypes:
          - "*/*"

このコードを記述し、保存後、コマンドプロンプトを起動し、「lambda」ディレクトリ内に移動し、「sls deploy」コマンドを用いてデプロイを行っても、まず「Error:
Deployment bucket has been removed manually. Please recreate it or remove your service and attempt to deploy it again」というエラーが発生するので、コードを変更する。

■コード(修正後その1)

service: web-selenium-lambda-test

provider:
  name: aws
  runtime: python3.6
  region: ap-southeast-2
  timeout: 900

functions:
  main:
    memorySize: 1000
    handler: handler.main
    events:
      - http:
          path: test
          method: get

    layers:
      - arn:aws:lambda:ap-southeast-2:{}:layer:chromedriver:2
      - arn:aws:lambda:ap-southeast-2:{}:layer:selenium:2

resources:
  Resources:
    ApiGatewayRestApi:
      Properties:
        BinaryMediaTypes:
          - "*/*"

「service: selenium-lambda」を「service: web-selenium-lambda-test」に変更し、保存する。保存後、デプロイする。デプロイするが、下記のエラーが発生する。

Error:
CREATE_FAILED: MainLambdaFunction (AWS::Lambda::Function)
Resource handler returned message: "1 validation error detected: Value '[arn:aws:lambda:ap-southeast-2:{}:layer:chromedriver:2, arn:aws:lambda:ap-southeast-2:{}:layer:selenium:2]' at 'layers' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 140, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: (arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+), Member must not be null] (Service: Lambda, Status Code: 400, Request ID: a54e6e64-7feb-4694-9417-a5b8b346872f)" (RequestToken: 048675f6-cb82-1fea-047e-b3464ad19e14, HandlerErrorCode: InvalidRequest)

このエラーが発生したので、問題の箇所を修正する。

■コード(修正後その2)

service: web-selenium-lambda-test

provider:
  name: aws
  runtime: python3.6
  region: ap-southeast-2
  timeout: 900

functions:
  main:
    memorySize: 1000
    handler: handler.main
    events:
      - http:
          path: test
          method: get

    layers:
      - arn:aws:lambda:us-east-1:*******:layer:chromedriver:2
      - arn:aws:lambda:us-east-1:*******:layer:selenium:2

resources:
  Resources:
    ApiGatewayRestApi:
      Properties:
        BinaryMediaTypes:
          - "*/*"

まず、providerの「region: ap-southeast-2」を「region: us-east-1 (米国東部(バージニア北部))」に変更します。今回、「Lambda」サービスはus-east-1 (米国東部(バージニア北部))を利用している。

※これは「chromedriver」のレイヤーで、参考として表示しています。

さらに「functions:」の「layers:」を下記のコードに変更する。このコードは、今回作成した「Lambda」の「レイヤー」の「selenium」、「chromedriver」をクリックすると、「バージョン」で「バージョン ARN」が表示されているので、こちらの情報をコピーして、コピーしたもので、上記にも記載していますが、下記のように変更します。

    layers:
      - arn:aws:lambda:us-east-1:****:layer:chromedriver:2
      - arn:aws:lambda:us-east-1:****:layer:selenium:2

変更後、保存します。保存後、、コマンドプロンプトを起動し、「lambda」ディレクトリ内に移動し、「sls deploy」コマンドを用いてデプロイを行います。

Warning: Invalid configuration encountered
at 'resources.Resources.ApiGatewayRestApi': must have required property 'Type'

Learn more about configuration validation here: http://slss.io/configuration-validation

Deploying web-selenium-lambda-test to stage dev (us-east-1)
Warning: Function main has timeout of 900 seconds, however, it's attached to API Gateway so it's automatically limited to 30 seconds.

✔ Service deployed to stack web-selenium-lambda-test-dev (138s)

endpoint: GET - https://****.execute-api.us-east-1.amazonaws.com/dev/test
functions:
main: web-selenium-lambda-test-dev-main (551 B)

1 deprecation found: run 'serverless doctor' for more details

Toggle on monitoring with the Serverless Dashboard: run "serverless"

デプロイを行うと、デプロイが完了し、上記のメッセージが出力されます。出力内容を確認すると「Warning(警告)」が出力されていますが、「エラー」ではないので、今回は一旦無視します。

「endpoint:」で「GET – https://****.execute-api.us-east-1.amazonaws.com/dev/test」」と出力されていますので、このURLをコピーします。コピー後、Webブラウザを起動し、アドレスバーに貼り付けて、アクセスします。

アクセスすると、AWSLambdaを介してSeleniumを実行し、指定したWebサイトのページタイトルを出力させることができました。

出力後、「AWS マネジメントコンソール」から「CloudFormation」を確認すると新しいスタックが追加されていることと、「S3」で新しいS3バケットが作成されていることが確認できました。

コメント

タイトルとURLをコピーしました