connpass API でIT勉強会の情報を手軽にランダム1件検索

## 前書き

多種多様に開催されているIT勉強会。
自分は開催情報を知るきっかけの一つに「connpass」をよく利用しています。

connpass - エンジニアをつなぐIT勉強会支援プラットフォーム
https://connpass.com/

IT勉強会を「Python」で検索してみました。
結果、251件ヒット。かなりの数のIT勉強会が開催されていますね。
![スクリーンショット 2019-07-24 19.42.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379795/ec2d4f84-9b9d-ab91-c06b-e6fa02ced399.png)


都道府県で絞ればもう少し数も絞れるとはいえ、
開催数が多くて、選び出すところでひと苦労してしまう。。。

自分はまさにそんな感じの人です orz


## 何がしたいのか

もっと積極的にIT勉強会へ参加する。
そのきっかけに「手軽なIT勉強会の検索処理」を作りたい！


## やったこと

connpass API からIT勉強会の情報を、
お手軽にランダム1件検索する処理を書いてみました。


## なぜランダム1件検索？

何が検索されるか分からない~~ガチャ的なw~~ワクワク感と、
検索を敢えて１件に絞って情報量を減らすことで、
検索結果をしっかり読むきっかけにできないかと考えました。


## connpass API

connpassはイベントサーチ用APIを提供しており、
誰でも利用することができるようになっています。
こちらを利用させていただきました。

connpass API
https://connpass.com/about/api/ 


## 実行結果

先に処理の実行結果から。
pythonコマンドで該当処理を「検索キーワード」をつけて実行します。
検索キーワードを「Python」にしてみます。

````
python connpass_search.py python
````

実行結果はこんな感じになりました。
（一応伏せ字にしましたが、実際の実行結果は全ての情報が閲覧可能です。）

````
---------------------------------------------
# (・∀・) connpassイベント 何がでるかな？
# 機能：ランダムで1件 イベントを検索します
---------------------------------------------

検索中・・・

■ 検索結果
イベント名：Fintech,Pyt…気。。。略
開　催　日：2019-07-24T19:3。。。略
場　　　所：東京都港区。。。略
会　　　場：Cre。。。略
詳細ページ：https://cuc。。。略
参加者定員：17
参　加　者：7
補　欠　者：0
主催グループ名 ：Cuc。。。略
主催グループURL：https://cuc。。。略

↑ピン！ときたら、ぜひ参加してみよう！
---------------------------------------------

````

また「検索キーワード」は「複数指定（AND検索）」もできます。
検索キーワードを「Python」「大阪」にしてみます。

````
python connpass_search.py python,大阪
````

実行結果はこんな感じになりました。
（一応伏せ字にしましたが、実際の実行結果は全ての情報が閲覧可能です。）

````
---------------------------------------------
# (・∀・) connpassイベント 何がでるかな？
# 機能：ランダムで1件 イベントを検索します
---------------------------------------------

検索中・・・

■ 検索結果
イベント名：阪医pyth。。。略
開　催　日：2019-07-26T1:。。。略
場　　　所：大阪府大阪市。。。略
会　　　場：PLU。。。略
詳細ページ：https://ou。。。略
参加者定員：30
参　加　者：17
補　欠　者：0
主催グループ名 ：阪医pyth。。。略
主催グループURL：https://ou。。。略

↑ピン！ときたら、ぜひ参加してみよう！
---------------------------------------------
````

検索結果は原則、ランダムになります。
コマンドを叩くたびに違う勉強会の情報を読めるのは、ちょっと楽しい！


## 実装

実装はこちらになります。
Python 3 で書いています。

````python
import sys
import random
import requests
import time
from datetime import datetime


def main():
    args = sys.argv
    keywords = []
    ym = ""

    print('---------------------------------------------')
    print('# (・∀・) connpassイベント 何がでるかな？')
    print('# 機能：ランダムで1件 イベントを検索します')
    print('---------------------------------------------')

    keywords, ym = _check(args)
    print('')
    print('検索中・・・')
    print('')
    event_data = _req(keywords, ym, 10)
    if len(event_data['events']) == 1:
        print('■ 検索結果')
    else:
        time.sleep(1)
        event_data = _req(keywords, ym, 1)
        if len(event_data['events']) == 1:
            print('■ 検索結果')
        else:
            print('(*_*;) 検索失敗 該当するイベントが見つかりませんでした')
            exit()

    for event in event_data['events']:
        if event['catch'] is not None:
            print('イベント名：' + event['title'] + ' ' + event['catch'])
        else:
            print('イベント名：' + event['title'])
        if event['started_at'] is not None:
            print('開　催　日：' + event['started_at'])
        if event['address'] is not None:
            print('場　　　所：' + event['address'])
        if event['place'] is not None:
            print('会　　　場：' + event['place'])
        if event['event_url'] is not None:
            print('詳細ページ：' + event['event_url'])
        if event['limit'] is not None:
            print('参加者定員：' + str(event['limit']))
        if event['accepted'] is not None:
            print('参　加　者：' + str(event['accepted']))
        if event['waiting'] is not None:
            print('補　欠　者：' + str(event['waiting']))
        # if event['description'] is not None:
        #     print('概　　　要：' + event['description'])
        if event['series'] is not None:
            if event['series']['title'] is not None:
                print('主催グループ名 ：' + event['series']['title'])
            if event['series']['url'] is not None:
                print('主催グループURL：' + event['series']['url'])
        print('')
        print('↑ピン！ときたら、ぜひ参加してみよう！')
        print('---------------------------------------------')


def _check(args):
    if len(args) == 1:
        print('(*_*;) INPUT ERROR：第1引数（検索キーワード）は必須です')
        exit()
    keywords = args[1]
    ym = datetime.now().strftime("%Y%m")
    if len(args) == 3:
        if (len(args[2]) == 6 or len(args[2]) == 8) and args[2].isdigit():
            ym = args[2]
        else:
            print('(*_*;) INPUT ERROR：第2引数（開催年月）の入力時は「yyyymm」または「yyyymmdd」の形式で入力してください')
            exit()
    return keywords, ym


def _req(keywords, ym, rand):
    params = {
        'keyword': keywords,
        'order': random.randint(1, 3),
        'start': random.randint(1, rand),
        'count': 1
    }
    if len(ym) == 6:
        params['ym'] = ym
    else:
        params['ymd'] = ym
    url = 'https://connpass.com/api/v1/event/'
    r = requests.get(url, params=params)
    return r.json()


if __name__ == '__main__':
    main()
````

## 実装詳細

まずはコマンドライン引数の仕様について。

上記例では「検索キーワード」だけを指定しましたが、
実際にはもう１つ「開催年月日」を指定できるようにしています。

「開催年月日」は毎回打つのが手間ですので、
省略時は「システム年月」を自動で設定するようにしました。
「開催年月日」を指定する時は「yyyymm」か「yyyymmdd」を入力します。


### コマンドライン引数関連の処理

````python
def _check(args):
    if len(args) == 1:
        print('(*_*;) INPUT ERROR：第1引数（検索キーワード）は必須です')
        exit()
    keywords = args[1]
    ym = datetime.now().strftime("%Y%m")
    if len(args) == 3:
        if (len(args[2]) == 6 or len(args[2]) == 8) and args[2].isdigit():
            ym = args[2]
        else:
            print('(*_*;) INPUT ERROR：第2引数（開催年月）の入力時は「yyyymm」または「yyyymmdd」の形式で入力してください')
            exit()
    return keywords, ym
````


続いて connpass API へのリクエストについて。
ランダム1件検索と謳っておりますが、正しくは以下の仕様としています。

````
1. 「更新日時順」「開催日時順」「新着順」のいずれかランダムの検索で
   「検索上位10件から1件」をランダム出力する

2. 上記1で該当がない（検索結果が10件以下の場合にランダム指定した数が検索数以上だった時）は
   １秒待ってから「更新日時順」「開催日時順」「新着順」のいずれかランダムの検索で
   「検索上位1件」に絞って出力する

3. 上記2で該当がない場合は「検索失敗」とする
````

ですので、ピンポイントな「検索キーワード」を指定した場合は、
connpass API へ２回リクエストを飛ばすこともある。という仕様になっています。

connpassAPI へのリクエストパラメータは params で定義しました。
概要はリファレンスを見ていただければ幸いです。
https://connpass.com/about/api/

（ちなみにリファレンスを見るとわかるのですが、
　先述の例で「Python」「大阪」で「大阪の勉強会」が検索できたケース。
　実際には「検索キーワード」は「イベント概要内の文字列」までを見にいきますので、
　「大阪」と入れたのに「東京の勉強会」がヒットした、といったケースもあり。
　（例：「東京の勉強会」だけど講師の方のプロフィールに「大阪出身」と書いていた…etc）
　何か良い解決方法はないものか・・・。）

### connpassAPI へのリクエスト関連処理

````python
    print('')
    print('検索中・・・')
    print('')
    event_data = _req(keywords, ym, 10)
    if len(event_data['events']) == 1:
        print('■ 検索結果')
    else:
        time.sleep(1)
        event_data = _req(keywords, ym, 1)
        if len(event_data['events']) == 1:
            print('■ 検索結果')
        else:
            print('(*_*;) 検索失敗 該当するイベントが見つかりませんでした')
            exit()

：
中略
：

def _req(keywords, ym, rand):
    params = {
        'keyword': keywords,
        'order': random.randint(1, 3),
        'start': random.randint(1, rand),
        'count': 1
    }
    if len(ym) == 6:
        params['ym'] = ym
    else:
        params['ymd'] = ym
    url = 'https://connpass.com/api/v1/event/'
    r = requests.get(url, params=params)
    return r.json()
````


そして最後に、検索結果の表示。
ここでは、やたら if で要素の存在確認を行っています。

その理由は、connpass イベントの必須入力項目は、
イベントの「タイトル」のみだからです。
![スクリーンショット 2019-07-24 20.32.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379795/24454ad1-f121-7aa6-c646-40959c325bb7.png)

だから「タイトル」以外はNULLがあり得る。チェックが必要と考えました。

それにしても、イベント名だけでイベント登録が完了する潔さ。
思わず自分もIT勉強会を企画してみようかと思ってしまう！
（って、そんな立派なネタないでしょorz）


### 検索結果の表示関連処理

````python
    for event in event_data['events']:
        if event['catch'] is not None:
            print('イベント名：' + event['title'] + ' ' + event['catch'])
        else:
            print('イベント名：' + event['title'])
        if event['started_at'] is not None:
            print('開　催　日：' + event['started_at'])
        if event['address'] is not None:
            print('場　　　所：' + event['address'])
        if event['place'] is not None:
            print('会　　　場：' + event['place'])
        if event['event_url'] is not None:
            print('詳細ページ：' + event['event_url'])
        if event['limit'] is not None:
            print('参加者定員：' + str(event['limit']))
        if event['accepted'] is not None:
            print('参　加　者：' + str(event['accepted']))
        if event['waiting'] is not None:
            print('補　欠　者：' + str(event['waiting']))
        # if event['description'] is not None:
        #     print('概　　　要：' + event['description'])
        if event['series'] is not None:
            if event['series']['title'] is not None:
                print('主催グループ名 ：' + event['series']['title'])
            if event['series']['url'] is not None:
                print('主催グループURL：' + event['series']['url'])
        print('')
        print('↑ピン！ときたら、ぜひ参加してみよう！')
        print('---------------------------------------------')
````


### まとめ

connpassAPIを使って勉強会情報を検索するようにしてみました。
情報量を敢えて減らすことで、公式サイトでの検索とは違った楽しさを味わえればと感じた次第。
~~何より黒画面だから仕事中にこっそり検索できるwww~~

こちらの実装は、git hub でも公開中です。
git hub には本件を対話型のインプットで組んだ処理もあげております。
荒削りにも程がありますけど、ご興味ある方はぜひどうそ。
https://github.com/masashi-sawai/python-learning/tree/master/tools/conpass_search


そんなわけで、
みなさまご自身の強みにつながる「良い勉強会との出会い」に、
本投稿が少しでも役立てば幸いです！