【AWS Lambda】ちょっとだけ凝ったTwitterBotを作った。【Python】


「今日の夕飯、何食べたい?」の返答に悩まされる皆様、こんにちは八重樫です。

食べるだけの人も、作る人も、「その日、何を食べるか」という事を考え悩み決める事は大変です。
それが、ほぼ毎日なんだから、これは本当に大変な事です。
また、それにリソースを割かれ続ける事はストレスになる事すらあります。

そこで、少しだけそれを緩和できそうなTwitterBotを作りました。
AWS(アマゾンウェブサービス)を使ってみて、色々とハマりどころもあったので備忘録を兼ねて書いておきます。


作ったBot紹介。

作ったBotはコチラ
毎朝7時にクックパッドのレシピをツイートします。
ただ、クックパッドのトップページに掲載されているオススメレシピをツイートしてるだけのTwitterBotは沢山あります。
なので、少しだけ凝った物にしました。

凝ったポイント。

  • ランダムに選出した旬の食材2種でレシピ検索。
  • 更に、検索結果からランダムでレシピを選出。

なので、レシピ被りがし難く、普段は使わない食材と出会える。
といった感じで、ほんの少しだけ日々が楽しくなる効果も期待出来ます。
また、検索→ランダム選出なので、普段では埋もれてしまうレシピとの出会いもあります。
こういったサイトでは、レシピ投稿者との出会いは大事。

毎朝ツイートされるので、朝ツイートを見て「コレは良いな」というレシピだったらツイートをお気に入りしておいて、仕事帰りに買い物をして夕飯に作るなんて使い方を想定しています。

基本的には毎朝1回のツイートのみなので、タイムラインを汚しません。
試しにフォローしてみて頂けると嬉しいです。


活用シーン

現在は食材リストの読み込みから検索をしていますが、例えばコレを「目玉商品✕利益率の高い商品」といった組み合わせにして、該当レシピの必要食材をプリインストールしたセット販売をするといった使い方も面白いと思います。
少し改変するだけでcsvではなくxmlやJSONにも対応出来るので、様々なシステムに組み込む事も多分可能。多分。
また、定期的なツイートだけではなく通常のTwitterの使い方も平行して行えるので、SNSマーケティングの一環にも。
商店を営む方、いかがでしょうか?


コード全文

Twitterを扱うライブラリはTweepyを使っています。特に理由はありません。
全体をdef handler():にしているのは、Lambdaの仕様。ローカルで試す際には最後にhandler()を追記して下さい。
また、リストの読み込みで日本語を扱っている為、PythonはPython3で。Python2だとエンコード/デコードでハマります。

import pandas as pd
from urllib.parse import urlparse,quote_plus,unquote
from urllib.request import urlopen
import tweepy
from bs4 import BeautifulSoup
import datetime
import random
import re

consumer_key = "TwitterのConsumerKey"
consumer_secret = "TwitterのConsumerSecret"
access_token = "TwitterのAccessToken"
access_secret = "TwitterのAccessSecret"

def handler():
    auth = tweepy.OAuthHandler(consumer_key,consumer_secret)
    auth.set_access_token(access_token,access_secret)
    api = tweepy.API(auth)

    Read_csv = pd.read_csv("file.csv",encoding="SHIFT-JIS") #旬の食材リスト読み込み

    month = (datetime.date.today().month)-1 #月取得

    links = [] #while用:リストの初期化
    LoopCnt = 0 #デバッグ用:ループ回数
    while links == []: #404が出なくなるまでループ
        search1 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))]) #旬の食材リストからランダムに検索キーワード1を抽出
        search2 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))]) #旬の食材リストからランダムに検索キーワード2を抽出

        while search1 == search2: #検索キーワードが同じだったら検索キーワード2を取り直す
            search2 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))])
        search_words = (search1 + " " + search2)

        search_url = ("https://cookpad.com/search/" + quote_plus(search_words,encoding="utf-8")) #クックパッド検索
        try:
            html=urlopen(search_url)
            soup = BeautifulSoup(html,'lxml')
            links = soup.find_all(class_="recipe-title font13 ")
        except:
            pass
        LoopCnt = LoopCnt + 1

    regex = r'recipe/\d*' #正規表現でレシピIDを探す
    pattern = re.compile(regex)
    recipeID = pattern.findall(str(links))

    url = 'https://cookpad.com/'+(recipeID[random.randint(1,len(recipeID))-1]) #検索結果からランダムにレシピを1つ抽出したリンク

    page = BeautifulSoup(urlopen(url),'lxml')
    title_data = page.find('h1')
    title = title_data.string

    Food_ingredients = ("旬の食材「"+search1+"」と「"+search2+"」を使ったレシピ")

    print (Food_ingredients + title + url)

    api.update_status(Food_ingredients + title + url)

旬の食材リストから検索

月毎の旬の食材リストをcsvで用意して、それをpandasで読み込み、ランダムに食材2種を選出してクックパッド検索します。

    Read_csv = pd.read_csv("file.csv",encoding="SHIFT-JIS") #旬の食材リスト読み込み

    month = (datetime.date.today().month)-1 #月取得

    links = [] #while用:リストの初期化
    LoopCnt = 0 #デバッグ用:ループ回数
    while links == []: #404が出なくなるまでループ
        search1 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))]) #旬の食材リストからランダムに検索キーワード1を抽出
        search2 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))]) #旬の食材リストからランダムに検索キーワード2を抽出

        while search1 == search2: #検索キーワードが同じだったら検索キーワード2を取り直す
            search2 = (Read_csv.iloc[(month),(random.randint(0,Read_csv.iloc[(month)].count()-1))])
        search_words = (search1 + " " + search2)

        search_url = ("https://cookpad.com/search/" + quote_plus(search_words,encoding="utf-8")) #クックパッド検索
        try:
            html=urlopen(search_url)
            soup = BeautifulSoup(html,'lxml')
            links = soup.find_all(class_="recipe-title font13 ")
        except:
            pass
        LoopCnt = LoopCnt + 1

食材リストからランダムに選出している為、検索結果が404エラーになる組み合わせもあります。
なので、検索結果のページにclass_=”recipe-title font13 “というhtmlタグが含まれていたらリストオブジェクトlinksにレシピタイトルを格納するようにして、このhtmlタグが含まれていなかった場合、再度検索するようにループ処理をしています。

検索結果に上記のタグが含まれていなかった際には、エラーを吐くのですが、面倒だったのでtry-exceptで無理やりエラー消しちゃってますが、検索ループの部分を関数にして、ifでループにした方がスマートな気がします。(他の言語ではifで囲いまくるのを避けている癖で、このように書いてしまっていますがPythonの場合はif多用の方が正解だと思います。多分。)

また、ランダムに選出する食材が同じ物だった場合も、食材選出をし直すように書きましたが、デバッグの際には食材被りをする事はなかったので、不要かも知れません。念の為。
一応、webスクレイピングに含まれると思われるので、クックパッドさんに怒られたらこのbot自体を削除します。


検索結果からレシピの選出

検索結果からもランダムにレシピの選出をします。
ランダムを多重で使う事で、レシピ被りが減る事に期待しています。

    regex = r'recipe/\d*' #正規表現でレシピIDを探す
    pattern = re.compile(regex)
    recipeID = pattern.findall(str(links))

    url = 'https://cookpad.com/'+(recipeID[random.randint(1,len(recipeID))-1]) #検索結果からランダムにレシピを1つ抽出したリンク

    page = BeautifulSoup(urlopen(url),'lxml')
    title_data = page.find('h1')
    title = title_data.string

    Food_ingredients = ("旬の食材「"+search1+"」と「"+search2+"」を使ったレシピ")

    print (Food_ingredients + title + url)

    api.update_status(Food_ingredients + title + url)

AWS Lambda

上記のコードでローカルでは動作します。
ですが、AWS Lambdaで動作させる際には色々とハマります。

先ず、Windowsマシンで開発している場合、色んな問題で上手くいかないので、さっさと諦めてVMwareか何かでUbuntuをインストールしましょう。
上手くいかないポイントとしては…

  • zipコマンドが使えない
  • Dockerを使う際にパスにC:が含まれる為、ローカルディレクトリの同期が出来ない。

の2点。
どちらも、ツールのインストールや、スクリプトを書けば解決できない事もないのですが、そんなんするぐらいならUbuntuインストールしちゃった方が楽。

ちなみに、Dockerはほぼ必須です。
AmazonさんがLambdaのDocker Imageを提供してくれているので、Docker使いましょう。

ちなみに、Dockerfileはこんな感じ。

FROM lambci/lambda:build-python3.6
ENV LANG C.UTF-8
ENV AWS_DEFAULT_REGION ap-northeast-1

WORKDIR /app
ADD . .

CMD pip install --upgrade pip && \
  pip install -r requirements.txt -t /app

pipのアップグレードをしてから、ライブラリのインストールをしています。
ライブラリのインストールはこんな感じ。

pandas
tweepy
bs4
lxml
datetime

lxmlが含まれているのがポイント。
Python3の標準ライブラリやPandasに含まれているはずと思うのですが、なんか知らんけどLambdaでエラーが出たので個別にインストールしています。numpyとlxmlはハマる。
ローカルではエラーにならないのですが、Lambdaだと出るので、発見し難い。

後はzipでアレしてS3にアップロードしてLambdaでイベントをアレするだけ。
ググればQiitaにちゃんとした解説がいっぱいあるので、そっちを参照して頂きたい。

毎朝1度のツイートのみなので、AWSの無料枠内に収まるはずです。多分。
不要になったコンピューターにUbuntuをインストールするだけで簡単に作れるので、とりあえず試してみるが可能。


Pythonでコストかけずに何か作りたかった。が、スタートでしたが何だかんだと結局Ubuntu使ったりDocker使ったり、AWS使ったりとコレを通して色々と学習になりました。
前回同様に、作ったっきりの物が多かったので「賞味期限の長い作品」を作れた。
Python✕AWS Lambda✕Twitterという組み合わせでまだネタがあるので、今後もチラホラと作っていきたく思っています。

外部サービスに依存した物なので、単体でのマネタイズは難しいですが、SNSマーケティングとりあえず初めてみたいなんて場合にローコストで導入が出来るかと思います。
何かあればお気軽にお問い合わせ下さい。

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>