AWS Lambdaで動的サイトのwebスクレイピングをしてtwitterに投稿するbotを作った

# はじめに

webページからスクレイピングをしたデータを簡単に加工して自動投稿するtwitterのbotを作りました。
完全に初見のLambdaとPythonでできました。
以下のサイトを組み合わせただけのほぼパクリ記事です。作業ログです。
[【Python】Chrome headlessで動的サイトのスクレイピング on AWS Lambda](https://qiita.com/mc-chinju/items/9fcfc70e7b6156312cfe#_reference-ed094e3864c95a8f3c72)
[AWS Lambda から Twitter に投稿する](https://www.ketancho.net/entry/2017/12/13/003000)

無駄に面倒なAPI Keyとアクセストークンの入手の方法についてですが、以下の記事を参考にしてください。
登録ページ:https://apps.twitter.com/
https://qiita.com/tdkn/items/521686c240b0c5bc6207
https://syncer.jp/Web/API/Twitter/REST_API/

## 追記(2019/12/23)
Python3系で実行し、容量を食うchroniumをLambda Layerに設定することでコンソールからも関数を確認できるようにして、
なおかつ、デプロイはServerless Frameworkでできるように改良しました。

[AWS Lambdaで動的サイトのwebスクレイピングをしてtwitterに投稿するbotを作った(続)](https://qiita.com/kihoair/items/4545f5a0f46f24ef9c3e)

# 環境
Cloud9
Python 2.7.10(3系でもいけるはずです)
Twitter developに登録してAPI Keyとアクセストークンを入手済み
AWSのアカウント

# ディレクトリ作成

requests_oauthlibを`pip install`したときの挙動がMacとAmazon Linuxでは違ったので以下パッケージのインストールはCloud9上で全て行います。
ということでまずはCloud9で適当に環境を作成します。
そして作業ディレクトリを作成します。

```
$ mkdir webscraping_tweetbot
$ cd webscraping_tweetbot
````

# seleniumインストール

作成したディレクトリにseleniumをインストールします。
`pip`でインストールするのですが、コマンドを実行したOS用のseleniumが自動でインストールされます。
今回は最終的にLambdaでプログラムを実行してもらうのでAmazon Linux用のseleniumが必要です。
[参考記事](https://qiita.com/mc-chinju/items/9fcfc70e7b6156312cfe#_reference-ed094e3864c95a8f3c72)に従ってバージョンも同じものをダウンロードします。

```
$ pip install selenium==3.13.0 -t .
```

後は作成されたディレクトリごとダウンロードして先ほどの作業ディレクトリの中に置きます。

# requests_oauthlibのインストール

作業ディレクトリ`webscraping_tweetbot`直下に認証ライブラリである`requests_oauthlib`をインストールします。twitterに接続する際に必要になります。

```
$ pip install requests_oauthlib -t ./
```

# beautifulsoup4のインストール

htmlを入手した後、データを整形するのにbeautifulsoupを使うので、`webscraping_tweetbot`直下にインストールします。

```
$ pip install beautifulsoup4 -t ./
```

# serverless-chromiumのダウンロード

binディレクトリを作成してそこにダウンロードします。

>最新版はPythonで動かない（2018年4月27日現在 [参照]）ため、v.0.0-37を入れます。
>
>引用: https://qiita.com/nabehide/items/754eb7b7e9fff9a1047d

```
$ mkdir bin
$ curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip
$ unzip headless-chromium.zip -d bin/
$ rm headless-chromium.zip
```

# Chrome driver のダウンロード

`serverless-chromium`と同様です。

```
$ curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip
$ unzip chromedriver.zip -d bin/
$ rm chromedriver.zip
```

これでライブラリのインストールは終わりです。

# lambda_function.pyの作成

lambdaにtweetを投稿するlambda_function.pyを作成します。
webスクレイピングそのものはwebscrape.pyのwebscrapeという関数にさせます。
beautifulsoupを使ったwebスクレイピングについては[こちらを参考](https://qiita.com/itkr/items/513318a9b5b92bd56185)
アクセストークンなどはLambdaの環境変数として設定します。

```python:lambda_function.py
#coding:utf-8

from requests_oauthlib import OAuth1Session
from webscrape import webscrape
import urllib3
import os

CK = os.environ['CK']
CS = os.environ['CS']
AT = os.environ['AT']
AS = os.environ['AS']

URL = 'https://api.twitter.com/1.1/statuses/update.json'

def lambda_handler(event, context):
    # webscrape.pyのwebscrapeの返り値(shapingdata(html))を受け取る。
    tweet=webscrape()
    
    session = OAuth1Session(CK, CS, AT, AS)

    params = {"status": tweet }
    twitter = OAuth1Session(CK, CS, AT, AS)
    req = twitter.post(URL, params = params)

    if req.status_code == 200:
        return tweet
    else:
        return req.status_code
```

# webscrape.pyの作成

```py:webscrape.py

#coding:utf-8

from selenium import webdriver
from bs4 import BeautifulSoup
import time

def webscrape():
    # webdriver settings
    options = webdriver.ChromeOptions()
    options.binary_location = "./bin/headless-chromium"
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--single-process")

    driver = webdriver.Chrome(
        executable_path="./bin/chromedriver",
        chrome_options=options
    )

    # web scraping
    driver.get("URL")
    #ここの時間が長いとlambdaの料金が無駄にかかります。driverがページの表示にかかる時間分だけを設定するのが良いです。
    time.sleep(0.3) 
    html = driver.page_source.encode('utf-8')

    return shaping_data(html)

def shaping_data(html):

    soup = BeautifulSoup(html, "html.parser")
    # 中略（beautifusoupの使い方に関しては他の記事を参照してください。）
    return tweet_data
```

# Zip化

webscraping_tweetbot上でLambdaにアップロードするファイルをzip化します。
あとはこれをダウンロードします。

```
zip -r webscraping_tweetbot.zip ./
```

# Lambdaで関数作成

Lambdaの関数を作成します。
twitterのbotを作るのでcloudwatch eventへのアクセス許可のIAMロールを割り当ててます。
ない場合は作りましょう。

<img width="842" alt="スクリーンショット 2018-10-29 12.08.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/277261/92bcc73c-8f08-24bc-0610-f87f39357521.png">

# lambdaにzipをアップロードしてデプロイ

作成したzipファイルをアップロードします。10MB以上ですがアップロードはできます。
S3に一度アップロードしてからアップロードをすることもできます。AWSはこちらの方法を推奨のようです。この場合はアップロードさえ終わればS3から消しても大丈夫です。
ハンドラのところは
_Lambdaで動かすPythonファイルへのパス.そのファイル内で実行する関数の名前_
を入力します。(今回はlambda_function.lambda_handler)

<img width="997" alt="スクリーンショット 2018-10-29 12.11.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/277261/4d263eb2-945c-b848-b4f0-0d82ed5b366c.png">

さらに画面下で環境変数を設定する項目があるのでそこでアクセストークンなどを(CK,CS,AK,AS)を忘れず設定しましょう。([参考](https://qiita.com/TakashiKOYANAGAWA/items/30352b288b23c8c8daae))

保存をしてテストを動かして無事グリーンになること確認したら、twitterにちゃんと投稿されているか確認します。

# Cloudwatch Eventsのルール適用

左側のリストからCloudWatch Eventsを選択します。

<img width="1011" alt="スクリーンショット 2018-10-29 12.17.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/277261/faf1647d-7848-75b8-4ce9-58777fadd70b.png">

すると下に設定画面が出てくるのでcron式またはrate式で投稿するスケジュールを設定します。

<img width="1297" alt="スクリーンショット 2018-10-29 12.24.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/277261/a43eaa82-90c8-261a-079a-38cac435ce51.png">

後は追加して、設定したスケジュールで投稿されたら終了です！お疲れ様でした。

# 作ったもの
ソースコードはこちら
https://github.com/kihoair/intro_bot/
まだ昨日(2018/10/28)からしか動いていないのですが、制作物です。
https://twitter.com/intro_tweetbot
個人的によく行くジャズバーのジャムセッションのスケジュールを1日1回自動でつぶやいています。メンバー見てからセッションに行きたくなったりすることってあるんですよね。それでは！