怠慢プログラマーの備忘録

怠慢でナマケモノなプログラマーの備忘録です。

【Python】AWSLambda上でSelenium+ChromeDriver+headless-chromeを使ってクローリングする

yutaabe200.hatenablog.com

この記事の通り、Golang+Agouti+ChromeDriver+headless-chromeを使ってAWSLambda上で動かすことに失敗してしまったので、諦めてPythonを使用することにしました。

なお、こちらの方法はGolangで行う際より参考記事が豊富ですが、私自身Python自体も未経験でしたので、Pythonの使用手順から備忘録を兼ねて記載します。

Python実行環境

MacにはPythonが最初からインストールされていますが少々バージョンが古いようです。

バージョンはターミナルより確認できます。

$ python --version

私の場合2.7.10が入っていましたのでこれを3系に上げる必要がありました。

なおAWSLambdaのランタイムは最近で3.7、以前より3.6が対応していたようです。

Pythonのバージョンを上げるにあたり、pyenvを使用します(Rubyのrbenvに相当するやつでしょう、きっと)。

github.com

Homebrew経由で取得が可能です。

$ brew install pyenv 
$ pyenv -v

bashを追加します。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

次にインストール可能なPythonのバージョンを検索します。

$ pyenv install --list

インストール可能なバージョンのものをインストールします。今回は3.6.5にしておきました。

$ pyenv install 3.6.5
$ pyenv versions
  system
* 3.6.5 (...)

最後にpyenv経由でインストールしたPythonを設定して終了です。

$ pyenv global 3.6.5
$ python --version
Python 3.6.5

AWSLambdaのデプロイパッケージ作成

デプロイパッケージにはGolangの際と同様にchromedriverとheadless-chromeが必要です。 取得方法は下記の記事をみてください。

yutaabe200.hatenablog.com

それに加えSeleniumが必要になります。

Selenium取得

pip経由で取得が可能です。自力で行うとjarを実行するみたいな雰囲気がありましたが、その方法はGolang->Pythonと歩んでる最中の私には到底手を出す気になりませんでした。

任意のディレクトリ内で以下を実行します。

$ pip install selenium -t  .

lambda-function.py

from selenium import webdriver

def lambda_handler(event, context):
    options = webdriver.ChromeOptions()
    options.binary_location = "./headless-chromium"
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--single-process")
 
    driver = webdriver.Chrome(
        executable_path="./chromedriver",
        chrome_options=options
    )

    driver.get("https://hogehoge/accounts/login/")
    title = driver.title
    url = driver.current_url

    return title + url

一旦ページタイトルとURLを返却するのみのコードです。 chromeoptionなどはGolangの際と同様かと思います。

ただLambda上のハンドル名はPythonの場合「ファイル名.関数名」の為、lambda-function.lambda.handlerになります。

この部分がGolangの時と少々異なりました。

またzipをアップロードした場合はLambdaのコンソール上でコードが編集できるそうですが、S3にアップロードしてしまうとGolang同様できなくなります。

SPA対策

上記コードでSPAサイトのDOMを単純に触りに行くとElementが見つからず例外が発生します。対象のサイトはReact製でしたので、jsで生成されるDOMの対応を行う必要があります。

その際の対応策としてSeleniumのWebDriverWaitを使用します。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

def lambda_handler(event, context):
    options = webdriver.ChromeOptions()
    options.binary_location = "./headless-chromium"
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--single-process")
 
    driver = webdriver.Chrome(
        executable_path="./chromedriver",
        chrome_options=options
    )

    driver.get("https://hogehoge/accounts/login/")
    title = driver.title
    url = driver.current_url
    print(title)
    print(url)

    try:
        WebDriverWait(driver, 100).until(EC.presence_of_element_located((By.NAME, 'username')))
        print("username is get")
        driver.find_element_by_name('username').send_keys('メールアドレス')
        print(driver.find_element_by_name('username').text)
        WebDriverWait(driver, 100).until(EC.presence_of_element_located((By.NAME, 'password')))
        print("password is get")
        driver.find_element_by_name('password').send_keys('パスワード')
        print(driver.find_element_by_name('password').text)
    except TimeoutException as te:
        print(te)
    finally:
        driver.close()

    return title + url

他の手段としてはPhantomJSを使用することもできるそうですが、必要でなければWebDriverWaitで済ませてしまおうかと思います。

独学プログラマー Python言語の基本から仕事のやり方まで

独学プログラマー Python言語の基本から仕事のやり方まで

入門 Python 3

入門 Python 3