AWS Lambdaで楽天証券の評価損益・時価総額のスクショを送る(Python 3.11)

AWS Lambdaで楽天証券の評価損益・時価総額のスクショを送る(Python 3.11)

概要

今回は、AWS Lambdaを利用して楽天証券のWebページから時価総額と評価損益のスクショを撮り、LINEで送信する方法を紹介します。

前提

  • 環境
    • MacBookAir(macOS 14.4, Apple Sillicon)
  • Docker導入済み
  • AWS CLIのセットアップ済み
  • Python 3.12

修正済みのソースを見たい方はこちらから。

https://github.com/himawari-aerobytes/scraping-rakuten-sec/tree/main

ECRのイメージからLambda関数を作成

SeleniumはWebブラウザの操作を自動化するフレームワークです。https://qiita.com/Chanmoro/items/9a3c86bb465c1cce738a

今回は、Seleniumを利用してChromeを操作して楽天証券の画面のスクショを撮ります。

Seleniumの利用には、Chromeのバイナリが必要になりますが、AWS LambdaではChromeのバイナリがデフォルトでは使えません。Chromeのバイナリは100MB近くの容量が必要となります。しかし、zipファイルでアップロードできるLambdaの容量は250MBが上限です。Pythonの諸々のライブラリを含めると、容量制限に引っかかってしまいます。

250MBを超える場合は、ECRでdockerイメージを利用してLambda関数を構築します。

ECRリポジトリの準備

まずは、AWSコンソールのECRのページに飛びリポジトリの作成を押下します

リポジトリ名に任意の名前(今回はselenium)を入力し「リポジトリを作成」を押下します

以下のような画面になれば成功です。ここで表示されているURIは以降使用するのでメモをしておいてください。

Dockerイメージのpush

この章では先程作成したECRリポジトリにDockerイメージをpushします。

Dockerfileの作成

Seleniumやchromedriverなどを導入したイメージを作成するためにDockerfileを作成します。

新規フォルダを作成し、Dockerfileを作成します。

開発環境が環境がMacではない場合、--platform=linux/amd64 の記述は削除してください。

Dockerfile

FROM --platform=linux/amd64 public.ecr.aws/lambda/python@sha256:fb31ca51357519a48a90f01a76e9d550778ecfcbe8d92dd832ec49b6672e387c as build
RUN dnf install -y unzip && \
    curl -Lo "/tmp/chromedriver-linux64.zip" "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.6045.105/linux64/chromedriver-linux64.zip" && \
    curl -Lo "/tmp/chrome-linux64.zip" "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.6045.105/linux64/chrome-linux64.zip" && \
    unzip /tmp/chromedriver-linux64.zip -d /opt/ && \
    unzip /tmp/chrome-linux64.zip -d /opt/

FROM --platform=linux/amd64 public.ecr.aws/lambda/python@sha256:fb31ca51357519a48a90f01a76e9d550778ecfcbe8d92dd832ec49b6672e387c
RUN dnf install -y atk cups-libs gtk3 libXcomposite alsa-lib \
    libXcursor libXdamage libXext libXi libXrandr libXScrnSaver \
    libXtst pango at-spi2-atk libXt xorg-x11-server-Xvfb \
    xorg-x11-xauth dbus-glib dbus-glib-devel nss mesa-libgbm
RUN pip install selenium==4.15.2
RUN pip install python-dotenv
RUN pip install boto3
RUN pip install requests
RUN dnf -y install langpacks-ja
RUN dnf -y install ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts
COPY --from=build /opt/chrome-linux64 /opt/chrome
COPY --from=build /opt/chromedriver-linux64 /opt/
COPY lambda/lambda_function.py ./
CMD [ "lambda_function.lambda_handler" ]

lambda_function.pyの作成

Lambdaで実行する関数を書きます。ファイル名はlambda_function.pyと必ずしてください。

後述しますが、Lambdaの環境変数として以下を設定しています。

キー
BUCKET_NAME画像を保存するS3バケット名
ID楽天証券のID
LINE_CHANNEL_ACCESS_TOKENLINE Messaging APIのチャンネルアクセストークン
PASS楽天証券のパスワード
USER_IDLINEのユーザID(送信先のユーザID)

lambda_function.py

import os
from dotenv import load_dotenv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
import time
import datetime
import boto3
from botocore.exceptions import NoCredentialsError, PartialCredentialsError
import requests
import json


# 現在時刻の取得
def getNowTimeString():
    # 時間の設定
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    return now.strftime("%Y%m%d%H%M%S")

def upload_to_s3(local_path, bucket_name, object_key):
    s3_client = boto3.client('s3')
    s3_client.upload_file(local_path, bucket_name, object_key)
    return f"https://{bucket_name}.s3.amazonaws.com/{object_key}"

def generate_presigned_url(bucket_name, object_key, expiration=3600):
    s3_client = boto3.client('s3')
    try:
        response = s3_client.generate_presigned_url('get_object',
                                                    Params={'Bucket': bucket_name,
                                                            'Key': object_key},
                                                    ExpiresIn=expiration)
    except (NoCredentialsError, PartialCredentialsError) as e:
        print("Credentials not available: ", e)
        return None
    return response

def send_LINE(now,local_path):
    line_channel_access_token = os.environ.get("LINE_CHANNEL_ACCESS_TOKEN")
    user_id = os.environ.get("USER_ID")
    bucket_name = os.environ.get("BUCKET_NAME")
    object_key = now+'_screenshot.png'

    # 画像をS3にアップロードしてURLを取得
    upload_to_s3(local_path, bucket_name, object_key)
    uploaded_image_url = generate_presigned_url(bucket_name, object_key)
    headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {line_channel_access_token}"
    }
        
    data = {
        "to": user_id,
        "messages": [
            {
                "type": "image",
                "originalContentUrl": uploaded_image_url,
                "previewImageUrl": uploaded_image_url
            }
        ]
    }
        
    response = requests.post(
        "https://api.line.me/v2/bot/message/push",
        headers=headers,
        data=json.dumps(data)
    )
        
    return {
        'statusCode': response.status_code,
        'body': response.text
    }

def lambda_handler(event,context):

    load_dotenv()

    # Optionの設定
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.add_argument("--single-process")
    chrome_options.add_argument("--disable-gpu")
    chrome_options.add_argument("--window-size=1280x1696")
    chrome_options.add_argument("--user-data-dir=/tmp/user-data")
    chrome_options.add_argument("--data-path=/tmp/data-path")
    chrome_options.add_argument("--disk-cache-dir=/tmp/cache-dir")
    chrome_options.add_argument("--homedir=/tmp")
    chrome_options.binary_location = "/opt/chrome/chrome"

    driver = webdriver.Chrome(options=chrome_options, service=Service("/opt/chromedriver"))

    driver.get('https://www.rakuten-sec.co.jp/ITS/V_ACT_Login.html') #特定のURLへ移動
    time.sleep(1)

    # ログイン処理
    loginId = driver.find_element(By.NAME, 'loginid')
    password = driver.find_element(By.NAME, 'passwd')

    loginId.clear()
    password.clear()

    loginId.send_keys(os.environ.get("ID"))
    password.send_keys(os.environ.get("PASS"))

    loginId.submit()

    time.sleep(5)
    allBreakdown=driver.find_element(By.ID,'homeAssetsTrigger')
    allBreakdown.click()
    time.sleep(5)
    portfolio = driver.find_element(By.ID, 'balance_data_actual_data')
    # 画面最大化
    driver.maximize_window()

    now = getNowTimeString()

    time.sleep(5)
    print(portfolio.location)
    local_path= '/tmp/'+now+'_screenshot.png'
    portfolio.screenshot(local_path)
    driver.quit()

    # LINE送信処理
    return send_LINE(now,local_path)

Dockerイメージの作成

Dockerfileとlamda_function.pyを作成したフォルダ内で以下のコマンドを実行します。

$ docker build -t selenium .

イメージをpushするために、タグをつけます。(URI は AWS ECR リポジトリ一覧に表示されていた URI に置き換えてください。)

$ docker tag selenium:latest 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/selenium:latest

ECRにログインするために、以下のコマンドを実行します(URI は AWS ECR リポジトリ一覧に表示されていた URI に置き換えてください。)

$ aws ecr get-login-password  | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com

イメージをECRにpushするために以下のコマンドを実行します。(URI は Amazon ECR リポジトリ一覧に表示されていた URI に置き換えてください。)

$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/selenium:latest

pushが完了すると、ECRリポジトリ内にlatestタグの付いたイメージが追加されていることが確認できます。

Lambda関数の作成

AWSコンソールのLambdaのページに飛びます。左上の「関数の作成」を押下します

「コンテナイメージ」を選択します。

「イメージの作成」から先ほど作成したECRのURIをペーストします。

その後、LambdaからS3にアップロードする権限を付与するために、IAMコンソールからIAMロールを作成します。

「サービスまたはユースケース」で、「Lambda」し、「次へ」を選択します。

「AmazonS3FullAccess」を許可ポリシーに追加し、「次へ」を選択します。

ロール名に、role-selenium-lambda(任意の名前)を入力し、「ロールの作成」を押下します。

Lambdaのページに戻り、「既存のロール」に先ほど作成したロールを設定し、「関数の作成」を押下します。

関数の作成が完了すると「正常に作成しました」と表示されます。

設定タブから「メモリ」を512MB、「タイムアウト」を2分0秒に設定します

環境変数に以下を設定します。

キー
BUCKET_NAME画像を保存するS3バケット名
ID楽天証券のID
LINE_CHANNEL_ACCESS_TOKENLINE Messaging APIのチャンネルアクセストークン
PASS楽天証券のパスワード
USER_IDLINEのユーザID(送信先のユーザID)

LINE Messaging APIとS3バケット名の取得

LINE Developersのページに行きます。https://developers.line.biz/console/

「プロバイダー」から「作成」を選択します

「プロバイダー名」にscraping-rakutensec-test(任意の名前)を入力します。

「作成」を押下します

「Messaging API」を選択します。

チャネル名、チャネル説明、大業種、小業種、メールアドレスを入力します。

「作成」を押下します。

「チャネルシークレット(環境変数のLINE_CHANNEL_ACCESS_TOKEN)」「あなたのユーザID(環境変数のUSER_ID)」を確認し、Lambdaの環境変数に登録します。

「Messaging API設定」のタブから「QRコード」を読み取り、LINE Botを友だち登録します。

AWSコンソールから、S3のページに飛びます。

「バケットを作成」を押下します。

「バケット名」に、名前を入力します。こちらが環境変数BUCKET_NAMEになります。Lambdaの環境変数にこちらも登録します。

「バケットを作成」を押下します。

以上で、手順は完了です。

Lambdaでテストを実行

LambdaでSeleniumのページに行き、「テスト」を実行するとLINEで画像が送られてきます。

Comments

No comments yet. Why don’t you start the discussion?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です