前書き
2016年もAdvent Calendarの季節が近づいてきましたね。
今年もニジボックスではQiitaを利用させてもらってアドベントカレンダーをやるのですが、
「自分のErrbot知見」というお題目を掲げて、
平日定時にアドベントカレンダーの枠状況を伝えるだけのBotスクリプトを用意してみました。
- 自分の好みでErrbotを使用
- すでに、なんとも言えない機能を持ったErrbotは稼働しているのでプラグインをつくるだけ
コードを晒す
from datetime import datetime
from errbot import BotPlugin, botcmd, arg_botcmd, webhook
import pytz
import requests
from bs4 import BeautifulSoup
from errcron.bot import CrontabMixin
from errcron.cronjob import CronJob
class Adventcalendar(BotPlugin, CrontabMixin):
"""
Qiita AdventCalendar
"""
# 主張1: crontabが使える
CRONTAB = [
'30 18 * * 1,2,3,4,5 .report_calendar_members',
]
TIMEZONE = 'Asia/Tokyo'
CHANNEL = '#general' # Qiita晒し用
CALENDAR_URL = 'http://qiita.com/advent-calendar/2016/nijibox'
def activate(self):
super().activate()
self.activate_crontab()
def report_calendar_members(self, polled_time):
"""デイリーで枠情報をしゃべる
"""
# 主張2: スクレイピングするとパース
resp = requests.get(self.CALENDAR_URL)
soup = BeautifulSoup(resp.content, 'html.parser')
items = soup.select('.adventCalendarItem')
# 主張3: 枠数と投稿者数を切り離す
reserved = [
item
for item in items
if len(item.select('.adventCalendarItem_author')) > 0
]
authors = set([
item.select('.adventCalendarItem_author')[0].text.strip()
for item in reserved
])
if len(reserved) < 25:
message = '{}\n{} 人によって {}個の投稿が掲載される予定です。\n引き続き参加のほど、よろしくお願いします。'.format(
self.CALENDAR_URL,
len(authors),
len(reserved),
)
channel = self.build_identifier(self.CHANNEL)
self.send(channel, message)
少しコードについての概説を書く
主張1: errcron によってcrontab的に毎日定時に喋ってもらう
以前書いた記事にあるように、Errbotにはcrontabのような機能はないので、
自分が作ったerrcron
を使って簡易的なCrontab的な動作をするようにしています。
そのため、
30 18 * * 1,2,3,4,5 .report_calendar_members
といった、crontab記法をそのまま使っています。
今回は休日まで喋らせてもうるさいので、平日の18:30という夜の区切りっぽい時間にしてみました。
主張2: requestsとBeautifulSoup4で手っ取り早くスクレイピング
今回、自社のアドベントカレンダーの情報はRSSからでも、APIとかでもなく、HTMLをそのままみに行っています。(RSSは公開記事しかもたないし、APIはそもそもないし)
requests
ならrequests.get
で一発でレスポンスとれすし、BeautifulSoupがあるので、「adventCalendarItem
クラスの要素を数える」というさっくりとした手段でカレンダーの各アイテムが数が取れました。
構造が綺麗なQiitaに感謝です。
主張3: 枠数と投稿者数を切り離す
今はそうでもないですが、一人のメンバーが複数の記事を書くこともあるので、一旦投稿者数を投稿数とは分けて考えてみることにしました。
投稿数について
.adventCalendarItem
なboxの中身はすでに枠が埋まっているときに限り.adventCalendarItem_author
クラスの投稿者名枠が出てきます。
つまり、Item
の中からauthor
の中身をとりあえず取りだせば、投稿リストがつくれます。
reserved = [
item
for item in items
if len(item.select('.adventCalendarItem_author')) > 0
]
重複を除去して、投稿者数にする
上記の投稿リストはから投稿者リストを重複なく作るのは直接だと面倒なので
- 投稿リストから名前を抽出してリストにする
- list -> setにして名前の重複を除去する
という手段で投稿者リスト(重複なし)を用意します。
authors = set([
item.select('.adventCalendarItem_author')[0].text.strip()
for item in reserved
])
最後にSlackに投げる
投稿数リストも投稿者リストもつくれたので、あとはそれを元にSlackにポストするだけです。
Errbotの標準機能なので、今回は細かい説明はパス。
これを見て、うちのカレンダーが賑やかになるといいですね。