はじめに
connpassの複数のイベントに参加していると、同一の内容を異なるグループのイベントでLTする機会も増えてきます。
エンジニアをつなぐIT勉強会支援プラットフォーム
https://connpass.com/
別のイベントではあるものの、同じ参加者がたくさん来られている場合は、同じ内容でLTするのは気が引けますよね。
connpassのイベント主催者は、参加者リストとcsv形式を取得することができます。しかし、一般の参加者は参加者一覧ページを閲覧することはできますが、csv形式では入手することができません。
そこで、指定したイベントの参加者リストをpython3でスクレイピングし、csv形式で出力する処理を作ってみました。
csvファイルの出力後は、イベント(2つのcsvファイル)間で、重複する参加者を出力する処理も用意しました。
GitHubにてソースコードを公開しています。
https://github.com/NobuyukiInoue/get_connpass_participants
参考1:スクレイピング処理に関しての留意事項
・Webスクレイピングの注意事項一覧
・connpass利用規約
参考2:動作確認環境
* Windows10 & Python 3.6.0
* Windows7 & Python 3.6.1
* macOS 10.13.6 & Python 3.6.5
* Ubuntu 16.04.5 LTS & Python 3.7.0
追記
2つのイベントを(CSVファイルに出力せずに)比較し、重複ユーザーを表示する処理も追加で作成しました。列挙したいだけなら、こちらの方が使い勝手が良いかもしれません。
ソースコード)get_participant_duplicates.py
1. 必要モジュールのインストール
HTMLのparse処理にbeautifulsoup4
を使用しています。
管理者権限でpip install beautifulsoup4
コマンドを実行し、事前にインストールしておいてください。
2. 参加者一覧の取得とcsvファイル出力
コンパスの各イベントのURLは下記のような書式になっています。
https://グループ名.connpass.com/event/イベントID/
「イベント参加者・申込者一覧」ページのURLには、participation
というサブディレクトリ名が付加されています。
https://グループ名.connpass.com/event/イベントID/participation/
2-1. 処理内容
参加者一覧を取得するスクレイピング処理では、この「イベント参加者・申込者一覧」ページにアクセスしてHTMLの応答を取得したあと、
<A HREF=...>
タグを抽出することにより、ユーザー名およびプロフィールへのリンクを取得しています。
import sys
from datetime import datetime
import urllib.request, urllib.error
from bs4 import BeautifulSoup
def print_arg_error(commandName):
"""引数エラー時のメッセージ出力"""
print("Usage: python {cmd} connpassURL\n"
"\n"
"example)\n"
"python {cmd} https://GROUP_NAME.connpass.com/event/xxxxx/"
.format(cmd=commandName))
exit(0)
def is_url(url):
"""URL書式チェック"""
return url.startswith("https://") or url.startswith("http://")
def participation_url(url):
"""参加者URLに変換"""
if url.endswith("/participation/"):
return url
if url.endswith("/participation"):
return url + "/"
if url.endswith("/"):
return url + "participation/"
else:
return url + "/participation/"
def getgrpName(url, extention):
"""グループ名とイベント番号を取り出してファイル名とする"""
# url format
# "https://<groupName>.connpass.com/event/<eventId>/participation/"
# <groupName>を取り出す
pos_l1 = url.find("/")
pos_l2 = url.find("/", pos_l1 + 1)
pos_l3 = url.find(".")
if ((pos_l1 < 0) or (pos_l2 < 0) or (pos_l3 < 0)):
print("groupName error...")
return ""
groupName = url[pos_l2 + 1:pos_l3]
# <eventId>を取り出す
pos_r1 = url.rfind("/")
pos_r2 = url.rfind("/", 0, pos_r1 - 1)
pos_r3 = url.rfind("/", 0, pos_r2 - 1)
if ((pos_r1 < 0) or (pos_r2 < 0) or (pos_r3 < 0)):
print("eventId error...")
return ""
eventId = url[pos_r3 + 1:pos_r2]
# 現在の時刻を年、月、日、時、分、秒で取得
dateStr = datetime.now().strftime("%Y%m%d_%H%M%S")
return(groupName + "_" + eventId + "_" + dateStr + extention)
def users(tags):
"""ユーザー情報の取得(ユーザ名とユーザURLのCSV形式)"""
for tag in tags:
try:
if tag.text and tag.text != "\n\n":
href = tag['href']
if "/open/" in href or "/user/" in href:
yield '"{name}","{url}"\n'.format(name=tag.text, url=href)
except:
pass
def main():
args = sys.argv
if len(args) <= 1:
print_arg_error(args[0])
# URL Format
# "https://xxxxxx.connpass.com/event/xxxxx/"
# URL書式チェック
if not is_url(args[1]):
print("URL Error...%s\n" %args[1])
print_arg_error(args[0])
# 参加者URLに変換
url = participation_url(args[1])
save_fname = getgrpName(url, ".csv")
if (save_fname == ""):
print_arg_error(args[0])
# 指定したURLの出力htmlを取得する
html = urllib.request.urlopen(url)
# htmlをBeautifulSoupに取り込む
soup = BeautifulSoup(html, "html.parser")
# すべての<A>タグを抽出する
tags = soup.find_all("a")
# ユーザー情報保存用リストに格納
ulist = list(users(tags))
# 抽出したリストを標準出力に表示する
for user in ulist:
print(user, end="")
# ファイルに出力する
with open(save_fname, mode='w') as f:
f.writelines(ulist)
print("\nsave as ... [%s]" %(save_fname))
if __name__ == "__main__":
main()
2-2. 取得処理の実行
実行時には、get_participant.py
への引数として、参加者リストを取得したいイベントのURLを指定します。
参加者リストは、実行結果として標準出力に表示されたあと、
グループ名_イベントID_YYYYmmdd_HHMMSS.csv
というファイル名で自動保存されます。
PS D:\> python .\get_participant.py https://グループ名.connpass.com/event/イベントID/
"(主催者01)","https://connpass.com/user/(主催者01)/open/"
"(発表者01)","https://connpass.com/user/(発表者01)/presentation/"
"(発表者02)","https://connpass.com/user/(発表者02)/presentation/"
"(参加者01)","https://connpass.com/user/(参加者01)/"
"(参加者02)","https://connpass.com/user/(参加者02)/"
"(参加者03)","https://connpass.com/user/(参加者03)/"
...
...
...
"(参加者29)","https://connpass.com/user/(参加者29)/"
"(参加者30)","https://connpass.com/user/(参加者30)/"
save as ... [グループ名_イベントID_YYYYmmdd_HHMMSS.csv]
PS D:\>
3. csvファイルの比較
次に、2つのイベント間で重複する参加者を抽出する処理です。
get_duplicate_user.py
で2つのイベントの参加者リストを読み込ませます。処理の中では、(ユーザー名ではなく)各ユーザーのプロフィールのURLが一致していないかを調べています。一致していた場合、そのレコードが表示されます。
(10行目のencoding="sjis"
は、必要に応じて書き換えてください。macOS
やLinux
では不要かも。)
3.1. 重複する参加者を抽出する
# coding: cp932
import sys
import os
def getDuplicateUsers(users1, users2):
"""2つのcsv配列を比較し、profile URLが一致するレコードを出力する"""
urls2 = {user2.split('","')[1] for user2 in users2}
for user1 in users1:
name1, url1 = user1.split('","')
if url1 in urls2:
yield user1
def main():
args = sys.argv
if len(args) <= 2:
print("Usage: python %s file1 file2" %(args[0]))
exit()
if ( os.path.isfile(args[1]) != True ):
print("%s not found." %(args[1]))
exit(0)
if ( os.path.isfile(args[2]) != True ):
print("%s not found." %(args[2]))
exit(0)
with open(args[1]) as f:
users1 = f.readlines()
with open(args[2]) as f:
users2 = f.readlines()
# 2つのcsv配列を比較し、profile URLが一致するレコードを出力する
print("\n=== Dupulicate users [%s] in [%s] ===" %(args[1], args[2]))
for user in getDuplicateUsers(users1, users2):
print(user, end="")
if __name__ == "__main__":
main()
3.2. 実行例
PS D:> python .\get_duplicate_user.py .\groupA_xxxxx_20180818_154841.csv .\groupB_yyyyy_20180819_185951.csv
=== Dupulicate users [.\data\groupA_xxxxxx_20180818_154841.csv] in [.\groupB_yyyyy_20180819_185951.csv]
===
"user01","https://connpass.com/user/user01/"
"user02","https://connpass.com/user/user02/"
"user03","https://connpass.com/user/user03/"
"user04","https://connpass.com/user/user04/"
PS D:\>