fbpx

Lambda上のSeleniumでスクリーンショットを撮った際に文字化けする問題の解消

AWSのLambda上でSeleniumを動かしていたのですが、スクリーンショットを作成する際に日本語フォントが文字化けするという問題が発生して困ったので、自分へのメモも兼ねて解決方法を記事にしました。

開発環境

  • Python 3.7
  • AWS Lambda
  • Selenium
  • headless-chromium
  • chromedriver

現在起こっている問題

フォントがきちんと表示されない

以下の画像のように、数字以外の日本語フォントの部分が全部□で表示されてしまっています。

修正前のコード

import boto3
import os
import time

print('test')

# python配下自動でimport 
from selenium import webdriver

def lambda_handler(event, context):
    
    URL = "https://cakutama.com"

    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    options.add_argument("--hide-scrollbars")
    options.add_argument("--single-process")
    options.add_argument("--ignore-certificate-errors")
    options.add_argument("--window-size=880x996")
    options.add_argument("--no-sandbox")
    options.add_argument("--homedir=/tmp")
    options.binary_location = "/opt/headless/python/bin/headless-chromium"

    #ブラウザの定義
    browser = webdriver.Chrome(
        executable_path="/opt/headless/python/bin/chromedriver",
        options=options
    )
    
    browser.get(URL)
    time.sleep(3)
    title = browser.title
    #browser.execute_script('''var style = document.createElement('style');style.textContent = `@import url('//fonts.googleapis.com/css?family=Source+Code+Pro');@import url('//fonts.googleapis.com/earlyaccess/notosansjp.css');`;document.head.appendChild(style);document.body.style.fontFamily = "'Noto Sans JP', sans-serif";document.querySelectorAll('pre > code').forEach((el, idx) => {el.style.fontFamily = "'Source Code Pro', 'Noto Sans JP', monospace";});''')
    time.sleep(3)
    browser.save_screenshot("/tmp/shot.png")
    browser.close()
    
    # S3アップロード
    s3 = boto3.resource('s3')
    s3.meta.client.upload_file('/tmp/shot.png', 'siteenginescreenshot', 'shot.png')
  

問題の原因

AWSのLambdaは日本語フォントに対応していません。そのため日本語を表示しようとすると上記のような文字化けが起こってしまいます。

解決方法

今回の解決方針

Lambdaのフォントに関する設定ファイルはいじることができないため、フォント設定のフォルダを変更して参照するか、DOMを書き換える必要があります。今回は比較的簡単にできるDOMを書き換える方針で行います。

コードの修正箇所

browser.execute_script('''
var style = document.createElement('style');
style.textContent = `@import url('//fonts.googleapis.com/css?family=Source+Code+Pro');@importurl('//fonts.googleapis.com/earlyaccess/notosansjp.css');`;
document.head.appendChild(style);
document.body.style.fontFamily = "'Noto Sans JP', sans-serif";document.querySelectorAll('pre > code').forEach((el, idx) =>{
el.style.fontFamily = "'Source Code Pro', 'Noto Sans JP', monospace";
});
''')

画面を読み込んだ後に以下の処理を追加します。

execute_scrip()というメソッドを使うのですが、このメソッドを実行すると()内に書かれたJavaScriptのコードが実行されます。今回はGoogleの日本語Webフォントを適用するJavascriptを実行しています。このコードが実行されたことにより日本語フォントが読み込まれるので正しく表示されます。

この方法が使えない例外

これはJavascriptの仕様上の問題なのですが、セキュリティの問題がありクロスドメインのiframeの内容は書き換えることができません。クロスドメインのiframeが含まれるサイトのスクリーンショットを保存したい場合は別の方法を検討してください。

まとめ

以下の記事でLambda上のSeleniumを使ってスクリーンショットを作成する方法を紹介しましたが、この記事のやり方だと英語のサイトであれば正しく表示されるのですが日本語のサイトが正しく表示されないという問題があることが分かりました。本記事では日本語のサイトでも文字化けせずに表示させる方法を紹介しました。今回紹介する方法はDOMの内容を書き換える方法なので、クロスドメインのiframeの中に関しては修正することができないのでご了承ください。

資料ダウンロード申し込み

Scroll to Top