概要
- LINEから画像が送信されるとMessagingAPIを使ってwebhookで画像を受け取る
- docomoの画像認識APIで画像に写っている商品を分析し、その結果をカルーセルで表示する
- docomoの画像認識APIで認識できる商品は、食品パッケージ/書籍/CD/DVD/ゲームソフト/PCソフトのみ
実際に作ったもの
- 下のボタン、もしくは
@sga5626f
でID検索すると友達に追加できます
- 画像を送るとこんな感じで分析結果を返してくれます
2018/7/12 追記
- docomoの画像認識APIのうち、オブジェクト認識APIは提供終了となりました
- よって、このbotはもう使えません(泣)
事前に準備すること
- LINEのbotを用意する
- webhookを受け取るアプリを作る
- Herokuにデプロイする
- LINEとアプリを連携する
上記については前に書いた記事 [python] LINEの文字起こし君を作ってみた と同じですので割愛します
docomoAPIのAPIキーを取得する
- # docomo Developer support に登録します
- APIの新規利用申請をします。必要事項を入力して、画像認識のAPIの利用申請をしましょう
- 利用申請が完了すると、APIキーが発行されます
docomoAPIを使って画像から商品を検索する
- # 画像認識API リファレンス に沿ってリクエストを送ります
-
candidates
がある場合は整形してlistの出力データを作ります -
category
によって返ってくるデータが若干違う(例: bookの場合はmakerやbrandがない等)ので注意してください - LINEのmessagingAPIで画像URLを送る場合、
https
しか受け付けないのでhttps
の場合のみimageUrl
を使うようにしています
docomo.py
import requests
import settings
API_KEY = settings.API_KEY
endpoint = 'https://api.apigw.smt.docomo.ne.jp/imageRecognition/v1/recognize'
def search_product(image=None):
if image is None:
return '必要な情報が足りません'
params = {
'APIKEY': API_KEY,
'recog': 'product-all',
'numOfCandidates': 5,
}
headers = {"Content-Type": "application/octet-stream"}
response = requests.post(
endpoint,
headers=headers,
params=params,
data=image,
)
status = response.status_code
data = response.json()
print(data)
if status != 200 and status != 204:
if status == 403:
error = '今月に使用できる回数を超過しました'
else:
error = 'エラーが発生しました'
print(status, data)
return error
candidates = data.get('candidates')
if not candidates:
return '商品が見つかりませんでした'
results = []
for content in candidates:
content['detail']['maker'] = \
content['detail'].get('maker') or \
content['detail'].get('publisher', '')
content['detail']['brand'] = \
content['detail'].get('brand') or \
content['detail'].get('author', '')
imageUrl = 'https://i.vimeocdn.com/video/443809727.jpg?mw=700&mh=394'
if content['imageUrl'].startswith('https'):
imageUrl = content['imageUrl']
result = {
"thumbnail_image_url": imageUrl,
"title": content['detail']['itemName'][:40],
"text": "{maker}: {brand}\n発売日: {releaseDate}"
.format(**content['detail']),
"actions": {
"label": "商品ページを見る",
"uri": content['sites'][0]['url']
}
}
results.append(result)
print(results)
return results
LINEでカルーセルテンプレートで返信する
- # TemplateSendMessage - CarouselTemplate を参考に次のようにカルーセルテンプレートを作成します
result = search_product(image)
if isinstance(result, str):
messages = [
TextSendMessage(text=result),
TextSendMessage(text='食品パッケージ/書籍/CD/DVD/ゲームソフト/PCソフトを検索できるよ!')
]
elif isinstance(result, list):
columns = [
CarouselColumn(
thumbnail_image_url=column['thumbnail_image_url'],
title=column['title'],
text=column['text'],
actions=[
URITemplateAction(
label=column['actions']['label'],
uri=column['actions']['uri'],
)
]
)
for column in result
]
messages = TemplateSendMessage(
alt_text='template',
template=CarouselTemplate(columns=columns),
)
- これを
index.py
に組み込むと、こんな感じになります
index.py
import os
from io import BytesIO
from flask import Flask, abort, request
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (CarouselColumn, CarouselTemplate, ImageMessage,
MessageEvent, TemplateSendMessage, TextMessage,
TextSendMessage, URITemplateAction)
import settings
from docomo import search_product
app = Flask(__name__)
line_bot_api = LineBotApi(settings.YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(settings.YOUR_CHANNEL_SECRET)
@app.route("/callback", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
# print("body:", body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError as e:
print("InvalidSignatureError:", e)
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
# print("handle_message:", event)
text = event.message.text
messages = [
TextSendMessage(text=text),
TextSendMessage(text='画像を送ってみてね!'),
]
reply_message(event, messages)
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
# print("handle_image:", event)
message_id = event.message.id
message_content = line_bot_api.get_message_content(message_id)
image = BytesIO(message_content.content)
try:
result = search_product(image)
if isinstance(result, str):
messages = [
TextSendMessage(text=result),
TextSendMessage(text='食品パッケージ/書籍/CD/DVD/ゲームソフト/PCソフトを検索できるよ!')
]
elif isinstance(result, list):
columns = [
CarouselColumn(
thumbnail_image_url=column['thumbnail_image_url'],
title=column['title'],
text=column['text'],
actions=[
URITemplateAction(
label=column['actions']['label'],
uri=column['actions']['uri'],
)
]
)
for column in result
]
messages = TemplateSendMessage(
alt_text='template',
template=CarouselTemplate(columns=columns),
)
reply_message(event, messages)
except Exception as e:
print("error:", e)
reply_message(event, TextSendMessage(text='エラーが発生しました'))
def reply_message(event, messages):
line_bot_api.reply_message(
event.reply_token,
messages=messages,
)
if __name__ == "__main__":
port = os.environ.get('PORT', 3333)
app.run(
host='0.0.0.0',
port=port,
)
完成!!
- 以上でLINEで画像を送ると商品検索するbotができました
- 正直そんなに検索に引っかかりません(涙)