環境
AWS LambdaとPythonでスクレイピング処理をマイクロサービス化する
上記記事で紹介している「headlessms」を利用する、下記のような「IDとパスワードでログインをして取得したクッキーを使って指定のURLから画像データをダウンロードし、そのバイナリを配列で取得して処理する」システムをAWS Lambdaで運用している。
import re,json,boto3,os
from base64 import b64decode
def lambda_handler(event, context):
kms = boto3.client('kms')
login_id = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_id']))['Plaintext'].decode('utf-8')
login_password = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_password']))['Plaintext'].decode('utf-8')
img_urls = []
img_urls = re.findall('https://bmimg.sample3.jp/image/chXXXXXXX/.+\.jpg',event['body'])
input_event = {
"body": open('func.py').read() \
.replace('__imgUrls__',str(img_urls)) \
.replace('__loginId__',login_id) \
.replace('__loginPassword__',login_password)
}
res = boto3.client('lambda').invoke(
FunctionName = 'headlessms-aws-function-00',
InvocationType = 'RequestResponse',
Payload = json.dumps(input_event)
)
img_ary = json.loads(json.loads(res['Payload'].read())['body'])
import requests,json
from base64 import b64encode
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
def scrape_process(driver):
driver.get('https://account.sample3.jp/login')
WebDriverWait(driver, 5).until(ec.presence_of_all_elements_located)
driver.find_element_by_id('input__mailtel').send_keys('__loginId__')
driver.find_element_by_id('input__password').send_keys('__loginPassword__')
submitButton = driver.find_element_by_id('login__submit')
submitButton.click()
session = requests.session()
for cookie in driver.get_cookies():
session.cookies.set(cookie['name'], cookie['value'])
img_urls = __imgUrls__
img_contents = []
for url in img_urls:
img_contents.append(b64encode(session.get(url).content).decode('utf-8'))
return(json.dumps(img_contents))
事象
あるとき、いつもなら問題ないく動作している上記システムの自動実行がboto3.client('lambda').invoke
の箇所で以下のようなエラーを出して不正終了していた。
{'errorType': 'Function.ResponseSizeTooLarge', 'errorMessage': 'Response payload size (9471685 bytes) exceeded maximum allowed payload size (6291556 bytes).'}
調査してみると、Lambda関数をRequestResponse
タイプで呼び出したときに送受信されるデータのサイズ上限が6291556 bytes
(約6MB)であり、超過した場合に発生するエラーとのこと。
そこで「headlessms」に送信されたURL数を確認してみると、いつもなら一度に10個程度の画像URL(img_urls
)しか送信されないところ、今回のエラーが発生したときの処理では32個もURLが送信されていた。
いつもより送信するURL数が多かったため、「headlessms」側で6MBを超える画像が取得され、返送時にエラーとなっていたらしい。
解決
lambda_main.py
を以下のように修正し、画像URLの「headlessms」への送信は最大10個ずつとなるようにした。
import re,json,boto3,os
from base64 import b64decode
def lambda_handler(event, context):
kms = boto3.client('kms')
login_id = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_id']))['Plaintext'].decode('utf-8')
login_password = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_password']))['Plaintext'].decode('utf-8')
img_urls = []
img_urls = re.findall('https://bmimg.sample3.jp/image/chXXXXXXX/.+\.jpg',event['body'])
def split_list(l):
for idx in range(0, len(l), 10):
yield l[idx:idx + 10]
img_urls_list = list(split_list(img_urls))
img_ary = []
for img_urls_within10 in img_urls_list:
#画像URLから画像データ取得
input_event = {
"body": open('func.py').read() \
.replace('__imgUrls__',str(img_urls_within10)) \
.replace('__loginId__',login_id) \
.replace('__loginPassword__',login_password)
}
res = boto3.client('lambda').invoke(
FunctionName = 'headlessms-aws-function-00',
InvocationType = 'RequestResponse',
Payload = json.dumps(input_event)
)
img_ary.extend(json.loads(json.loads(res['Payload'].read())['body']))
参考
https://forums.aws.amazon.com/thread.jspa?threadID=230229
https://www.stackery.io/blog/RequestEntityTooLargeException-aws-lambda-message-invocation-limits/
https://www.python.ambitious-engineer.com/archives/1843
以上