自己紹介
普段は渋谷のフィンテックベンチャーでセールスマネージャーをしており、プログラミングとは無縁の仕事をしています。ビジネスサイドが主戦場なので、物を作れる人に対する大きな憧れがあったのと、日々の業務の中でも、自分自身のシステムに対する理解の浅さから、エンジニアに迷惑をかけてしまうことがありました。また、技術的知識不足から、競合とのコンペで負けてしまい、悔しい思いをしたことがあったので、プログラミングの勉強をすることにしました。
はじめに
2月からRubyの勉強を始め、今回の10連休で簡易的なサービスを作れればと思い、UIの設計が不要なSlack botを作ることにしました。
写真のように、botにメンションを飛ばすと、botが反応し、カテゴリを選ぶと、AmazonのTOP10を表示してくれます。
詳細のコードはGithubにて共有しております。rubyのバージョンは2.3.0
で作っています。
#開発の大まかな流れ
- 必要なデータをAmazonから取得する
- Slackとの連携
- Slack内でのUIの作り込み
Amazonから必要なデータをスクレイピング
今回はAmazonが提供しているAPIではなく、スクレイピングでデータを取得しています。
require 'mechanize'
今回は mechanize
というgemを使ってスクレイピングを進めていきます。
# Amazon.comの対象カテゴリのランキングをスクレイピングで取得する
def get_amazon_ranking(category)
agent = Mechanize.new
agent.user_agent_alias = "Windows Mozilla" #書かないとエラーが起こるおまじない的なもの。
page = agent.get("#{get_amazon_ranking_url(category)}")
ここでは、変数categoryのデータを引っ張ってくる指示を飛ばしています。
# カテゴリー指定
def get_amazon_ranking_url(category)
if category.include?('ビジネス・経済')
url = 'https://www.amazon.co.jp/gp/new-releases/books/466282'
else
exit
end
return url
end
カテゴリー指定は上記の様な形で行い、今後、入力したいカテゴリーを増やす際は、上記にカテゴリー名とURLを入力し、if文を作るだけで追加出来ます。
データをタイトルをkeyに、URLをvalueにしてハッシュへ変換していきます。
# XMLを配列にする(Titles)
def convert_array_from_xml_titles(xml_titles)
titles = []
xml_titles.each_with_index do |xml_title, i|
titles << "#{i + 1}:#{xml_title.inner_text.gsub(/\r\n|\r|\n|\s|\t/, "")}"
break 1 if i == 9
end
return titles
end
# XMLを配列にする(URLs)
def convert_array_from_xml_urls(xml_urls)
urls = []
xml_urls.each_with_index do |xml_url, i|
# レビューのURLもClass名が同じなので、取得しない
# 不要なクエリパラメータを削除する
url = xml_url.get_attribute('href').match(/dp\/[a-zA-Z0-9]+/).to_s
# レビューを除いた際に、空白が入ってしまうので削除する
if url != ''
urls << 'https://www.amazon.co.jp/' + url
end
break 1 if i == 9
end
return urls
end
それぞれ、convert_array_from_xml
という形で変数を定義し、配列を用意します。Urlsの部分では、本に対するレビューのURLも同じClass名の中に入っていたため、不要なクエリパラメータを削除するための処理を行っています。
タイトル、URLともに、10個取り出せた時点でbreakする設定をしています。ここで回したデータを、TitlesとUrlsに返し、指定したクラスからデータを抜き出します。
# 必要な項目だけを抜き出す&XMLを配列にする
titles = convert_array_from_xml_titles(page.search('.p13n-sc-line-clamp-2'))
urls = convert_array_from_xml_urls(page.search('.a-col-left .a-link-normal'))
rankings = {}
titles.zip(urls) do |title, url|
rankings[title] = url
end
return rankings
end
ここで更にrankingsという連想配列を用意し、.zipメソッド を活用して、TitleとURLがSlackに交互に出力される指示を出します。
Slackとの連携
まず、slack-ruby-client
というgemをインストールし、ひな形をコピペします。
require 'slack-ruby-client'
Slack.configure do |conf|
conf.token = 'xoxb-*****************' # トークンは後ほど取得します。
end
# RTM Clientのインスタンス生成
client = Slack::RealTime::Client.new
# Slackに接続できたときの処理
client.on :hello do
puts 'connected!'
client.message channel: 'your_channel_id', text: 'connected!'
end
# ユーザからのメッセージを検知したときの処理
client.on :message do |data|
if data['text'].include?('こんにちは')
client.message channel: data['channel'], text: "Hi!"
end
if data['text'].include?('かしこい') || data['text'].include?('えらい')
client.message channel: data['channel'], text: "Thank you!"
end
if data['text'].include?('おやすみ')
client.message channel: data['channel'], text: "Good night"
end
end
# Bot start
client.start!
次に、ブラウザ上のSlackで Bots
から、botを作成します。
https://xxxxxxxxxxx.slack.com/apps/search?q=bots
client.message channel: 'your_channel_id', text: 'connected!'
上記の部分を今回表示したい自分のSlackのチャンネルに変える作業を行います。
xoxb-*****************
の部分に、bot作成後のアクセストークンを貼ると、繋込みがされます。
Slack内でのUIの作り込み
Slack内での挙動に関しては、@レイワーくん
とスラックで呼び出すと、「僕は現在のAmazonランキング10選を教えることができるよ!★をつけてカテゴリを選んでください!」→「カテゴリー一覧」という流れで、反応するインターフェイスにしました。カテゴリー名をユーザーが入力したかどうかを検出するため、各カテゴリーの頭に★マークを入れ、★があるかどうかで、カテゴリー名と紐づけたデータを引っ張ってくる処理をif文で書いています。
# ユーザからのメッセージを検知したときの処理
client.on :message do |data|
if data['text'].include?('レイワーくん') || data['text'].include?('<@UJA1HUXEG>')
client.message channel: data['channel'],
# 追加していく!
text: "僕は現在のAmazonランキング10選を教えることができるよ!\n★をつけてカテゴリを選んでください!\n```★ビジネス・経済 ★コンピュータ・IT ★科学・テクノロジー ★エンターテイメント ★歴史・地理 ★教育・学参・受験 \n★文学・評論 ★社会・政治 ★家電&カメラ ★ホーム&キッチン ★ホビー ★パソコン・周辺機器 ★ゲーム ★おもちゃ```"
end
# FIXME
# メッセージの中に★があれば、カテゴリとしてみな
if data['text'].include?('★')
rankings = get_amazon_ranking(data['text'])
rankings.each{|title, url|
client.message channel: data['channel'], text: "#{title} \n#{url}"
}
end
end
Herokuへのアップロードの過程はこちらの記事で紹介しております。
#作り終えての感想
簡単に作れるかと思いきや、XMLの変換や、スクレイピングでノイズを除去する作業で、思った以上に苦戦しました。一方、エラーを自力で解決出来たときの快感や、作ったものを人に見せて褒めてもらえたときの喜びは、これまでにあまり経験したことのない感動でした。未知の世界で自分の非力さを実感しながら格闘出来たことも非常に価値のある経験でした。
制作過程で死にそうな自分を何度も助けてくださった@IZUMIRU0313さんには感謝しかないです。早くエンジニアの先輩方とも肩を並べて、共闘できるくらいの戦闘力を身に着けていけるよう精進していきます。
サービスに関して、質問があれば、@matsukazu1995g1までご連絡ください!
#参考記事
- [Slack Botの種類と大まかな作り方]
(https://qiita.com/namutaka/items/233a83100c94af033575) - [SlackでBotに何か喋らせたくて、Rubyでコード書いてたら、いくつもエラーが出たけど、奮闘してたらやっと動いた話。]
(https://qiita.com/nekonekonekoe/items/b39cc763945bdcef35fb) - [Rubyリファレンス zip (Enumerable)]
(https://ref.xaio.jp/ruby/classes/enumerable/zip) - [【第一回】超簡単!RubyでSlack Botを作る方法]
(http://studio-andy.hatenablog.com/entry/ruby-bot) - [RubyでXMLファイルをHashとしてパースする方法の比較]
(https://qiita.com/tamano/items/4a6ea57bdb182a0bccce)