- 概要
- arXivのAPIを通して、設定したキーワードに関連する論文を取得
- Incoming Webhooksを通して、対象となる論文をSlackに投稿
- cronを用いて定期実行できるようにする。
- 実行例
概要
SlackのIncoming Webhooksという機能を使うと、簡単にプログラム上からSlackに投稿ができます。その機能を用いて、Arxivから興味のある論文を自動で検索して投稿してくれるbotを作りたいと思います。実装したプログラムはここに置いています。
https://github.com/kushanon/oreno-curator/blob/master/arxiv.py
開発環境はAnaconda2-4.3.0を用いています。
類似の機能を提供しているサービスとしては、Google Scholarのアラート機能があり、それを使うと設定したキーワードに関連する論文が投稿されたらメールでアラート送ってきてくれます。
それと比較して、今回提案するやり方の嬉しいところとしては、
- Slack上のbotなので、botを既存の研究開発関連チャンネルに追加しておくことでスムーズな議論を行うことができる。僕の場合も、チームで研究開発をする際に、開発している内容に関連する情報を適宜教えてくれる用途で使っています。
- 自分でプログラムを自由に書き換えられるので融通がきく
となっています。
1. arXivのAPIを通して、設定したキーワードに関連する論文を取得
arXivのAPIについての説明は、ここに概要が載っていて、ここに細かい仕様が載っています。import urllibFrom https://arxiv.org/help/api/index
url = 'http://export.arxiv.org/api/query?search_query=all:electron&start=0&max_results=1'
data = urllib.urlopen(url).read()
print data
こんな感じでHTTPのGETやPOSTリクエストを適切なURLに送ることでAPIを利用することができます。
そして、URL中のパラメータを変更することで取得する情報を変更することができます。
詳しい説明は先述のUser Manualを参照していただきたいのですが、このURLのパラメータに取得したい情報を記述します。
search_queryの部分で引っ張ってくる論文の選び方を指定できます。例えばsearch_queryで、ti:Machine\ Learningとうつと、Machine Learningという単語がタイトルに入っている論文を引っ張ってきてくれます。ti以外のprefixも色々と用意されています。例えば、Abstractはabs、catはarXivでのカテゴリーです。arXivでのカテゴリーについてもUser Manualにくわしい記載があります。
また、Boolean演算子やグルーピング演算子も使えるので細かい指定も可能です。
例えば、サンプルプログラムにあるように、
(cat:stat.ML+OR+cat:cs.CV+OR+cs.HC+OR+cs.IR)+AND+((abs:emotion)+OR+(abs:ECG)+OR+(abs:time\ series))のようにうすると、
カテゴリーが、stat.ML、cs.CV、cs.HC、cs.IRのいずれかで、アブストの中にemotion、ECG、time seriesという単語のいずれかが含まれる論文を検索することができます。
2. Incoming Webhooksを通して、対象となる論文をSlackに投稿
https://my.slack.com/services/new/incoming-webhook/にアクセスしてもらうと、
こんな画面が出てくると思います。
①の箇所でbotを追加したいSlackチームを選んで、
②の箇所でbotを追加したいチャンネルを選んで、
③を押します。
すると以下のようなページが出てきます。
④にURLがあるのでそれをメモしてください。

ちなみに、このページに書かれて居るように、botの見かけや、アカウント名を変えたり、表示する文字をカスタマイズできたりします。
PythonでこのURLにポストする場合こんな感じでかけます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
requests.post(api_url, json={"text": "Hello!!"}) |
api_urlの部分に先程メモしたURLを貼り付けて実行すると、設定したSlackチームのチャネルにHello!と投稿されます。
以上をまとめたPythonのプログラムは
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import requests | |
import pickle | |
import os | |
def parse(data, tag): | |
# parse atom file | |
# e.g. Input :<tag>XYZ </tag> -> Output: XYZ | |
pattern = "<" + tag + ">([\s\S]*?)<\/" + tag + ">" | |
if all: | |
obj = re.findall(pattern, data) | |
return obj | |
def search_and_send(query, start, ids, api_url): | |
while True: | |
counter = 0 | |
# url of arXiv API | |
# If you want to customize, please change here. | |
# detail is shown here, https://arxiv.org/help/api/user-manual | |
url = 'http://export.arxiv.org/api/query?search_query=' + query + '&start=' + str(start) + '&max_results=100&sortBy=lastUpdatedDate&sortOrder=descending' | |
# Get returned value from arXiv API | |
data = requests.get(url).text | |
# Parse the returned value | |
entries = parse(data, "entry") | |
for entry in entries: | |
# Parse each entry | |
url = parse(entry, "id")[0] | |
if not(url in ids): | |
title = parse(entry, "title")[0] | |
# abstract = parse(entry, "summary")[0] | |
date = parse(entry, "published")[0] | |
message = "\n".join(["=" * 10, "No." + str(counter + 1), "Title: " + title, "URL: " + url, "Published: " + date]) | |
requests.post(api_url, json={"text": message}) | |
ids.append(url) | |
counter = counter + 1 | |
if counter == 10: | |
return 0 | |
if counter == 0 and len(entries) < 100: | |
requests.post(api_url, json={"text": "Currently, there is no available papers"}) | |
return 0 | |
elif counter == 0 and len(entries) == 100: | |
# When there is no available paper and full query | |
start = start + 100 | |
if __name__ == "__main__": | |
print("Publish") | |
# Set URL of API | |
# Please change here | |
api_url = API_URL | |
# Load log of published data | |
if os.path.exists("published.pkl"): | |
ids = pickle.load(open("published.pkl")) | |
else: | |
ids = [] | |
# Query for arXiv API | |
# Please change here | |
query = "(cat:stat.ML+OR+cat:cs.CV+OR+cs.HC+OR+cs.IR)+AND+((abs:emotion)+OR+(abs:ECG)+OR+(abs:time\ series))" | |
start = 0 | |
# Post greeting to your Slack | |
requests.post(api_url, json={"text": "Hello!!"}) | |
# Call function | |
search_and_send(query, start, ids, api_url) | |
# Update log of published data | |
pickle.dump(ids, open("published.pkl", "wb")) |
既にSlackに投稿したものはログをとっておき投稿しないようにし、arxivのstat.ML, cs.CV, cs.HC, cs.IRカテゴリから,アブストの中にemotion, アブストの中にECG, アブストの中にtime seriesが入っている最新の論文から順番に10本ずつSlackに投稿してくれるような仕組みになっています。 これをcronを用いて定期実行できるようにします。
3. cronを用いて定期実行できるようにする。
cronはスクリプトを自動実行できるデーモンプロセスで、Unix系OSではデフォルトで入っています。詳しくはhttp://www.server-memo.net/tips/crontab.htmlなどに書かれています。
注意すべき事項としては、cronではrootからのpathでアクセスされるので、上記プログラムではプログラムを実行する前に、cdをしてプログラムが置いてあるディレクトリに移動する必要があります。また、PyenvやAnacondaなどの環境を用いている場合は、
/home/***/.pyenv/versions/anaconda2-4.3.0/bin//python arxiv.py
のように、Pythonの実行ファイルを指定する必要があります。
実行例
実行するとこんな感じで投稿してくれます。
pythonかっけー
返信削除