Python+Seleniumを利用する【Slackでarxiv botを作成してみる編】

[追記]
何か質問、修正、提案があれば気軽にコメントしてください。
どんどんいいものを作って行きたいので、是非お願いします…！
[追記終わり]

毎日arxivをチェックしようと意気込んでいるのですが、なんだかんだ忘れてしまうことが多いのでいっそのこと毎日Slackにでも更新情報を流してくれるbotでも作っちゃえということで作成しました。

#こんな人におすすめ
・スクレイピングやってみたい人(seleniumとか)
・pythonでbot作ってみたい人
・arxivをチェックするの忘れる人
・arxivから興味のある論文だけを教えて欲しい人(今回やりませんが、正規表現などで簡単にできると思います。)

#こんな感じ
arxivの新規論文を毎日9時にSlackに通知してくれます。
僕の場合、日本語訳がある方がぱっと見で分かりやすいのでGoogle翻訳による日本語訳も一緒に出してます←

通知内容はこんな感じです。
![スクリーンショット 2018-06-15 14.35.07.png](https://qiita-image-store.s3.amazonaws.com/0/210451/f25fe8d2-c88c-6d1b-5c89-127bb1f90350.png)

#インストールとセッティング
・python3系
・slackアカウント
・google chrome
ここらへんは既に使われてることが多いと思います。

##seleniumのインストール
今回自動でwebページを巡回するために利用するツールです。
pythonのパッケージとしてサポートされてるのでpipでインストールします。

```
pip install selenium
```

##chromedriverのインストール
SeleniumがChromeを操作するためのドライバーです。
macならhomebrew、linuxならlinuxbrewでインストールできます。
他OSでも[ここ](http://chromedriver.chromium.org/downloads)からダウンロードできます。

```
brew install chromedriver
```

##slack webhookの設定
slackに外部から投稿するためのAPIとしてWebHooksを利用します。
[SlackのIncoming Webhooksを使い倒す](https://qiita.com/ik-fib/items/b4a502d173a22b3947a0) にWebHooksの取得の仕方が記載されてるので、「Incoming Webhooksのエンドポイントを取得する」の項を順に行って設定してください。

ここで、記事にあるようにWebhookURLをメモしておいてください。 ...(1)

#Pythonコーディング

実際のコードとその説明を載せます。

```python:arxivbot.py
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
```
seleniumをインポート

```python:arxivbot.py
options = Options()
# ヘッドレスモードで起動。
options.add_argument('--headless')

# ChromeのWebDriverオブジェクトを作成する。
# Cronで動かす際には特にexecutable_pathが必要
driver = webdriver.Chrome(executable_path="/usr/local/bin/chromedriver", chrome_options=options)
```

seleniumでgoogle chromeを起動。
毎回ブラウザが開くと閉じるのがめんどくさいのでheadlessモードで起動します。
executable_pathには

```
which chromedriver
```
の結果を書いてください。僕の場合は/usr/local/bin/chromedriverでした。

```python:arxivbot.py
import slackweb

# webhookのURL
WEBHOOK_URL = "https://hooks.slack.com/services/*******/*******/***************"
# webhookをセット
slack = slackweb.Slack(url=WEBHOOK_URL)
```

(1)で確認したWebhookのURLを入れて(WEBHOOK_URL="ここに入れる")、slackwebにセットします。
slackwebがない場合は

```
pip install slackweb
```

でインストール。

```python:arxivbot.py
rss = feedparser.parse("http://export.arxiv.org/rss/cs.CL")
```
arxivの更新情報は毎日RSSという形式で更新されていきます。
[arxivの公式ページ](https://arxiv.org/help/rss)から詳細が見れます。
例えばComputer ScienceのComputation and Languageの論文の情報が知りたいときには
http://export.arxiv.org/rss/cs.CL
を見ればいいことになります。
興味のある分野に合わせてparse("ここ")を変えてください。

ここからがスクレイピングのコードです。

```python:arxivbot.py
#英語のタイトルを日本語に翻訳する関数。
def translate(summary):
    #Google翻訳を開く
    sleep(3)
    driver.get("https://translate.google.com/?hl=ja")

    #英語を打ち込む
    driver.find_element_by_id("source").send_keys(summary)

    #"翻訳"ボタンを押す
    driver.find_element_by_id("gt-submit").click()

    #翻訳待ち
    sleep(3)

    #翻訳後の日本語取得
    result = driver.find_element_by_id("result_box").text

    return result


#論文ごとのクラス
class Entry():
 
    def __init__(self,entry):
        #著者、URL、タイトル、要約を取得。
        self.author = [re.sub("<.+?>","",a) for a in entry.author.split(", ")]
        self.url = entry.id
        self.title = re.sub(" \(arXiv:.+\)$","",entry.title)
        self.author_info = {}

    #タイトルと要約を英日翻訳
    def translate(self):
        #translate by google translation
        self.title_ja = translate(self.title)
        self.summary_ja = translate(self.summary)

#RSSのエントリー(新規論文)ごとに翻訳
for one_entry in rss.entries:
    try:
        # Entryのクラスを作成
        entry = Entry(one_entry)
        # 翻訳
        entry.translate()
```

新規論文の情報はrss.entriesにリスト形式で格納されてます。
なのでこれをfor文で回せば1論文ごとの情報が取得できます。
今回はクラスを使用して、initで著者、URL、タイトル、要約を取得しておきます（もちろんクラス使わなくてもOK）。
次に翻訳ですが、上のtranslate関数で可能です。
翻訳の際のseleniumの動きがどうなってるか気になったら、headlessモード以外で起動してやってみてください。

seleniumの使いかたは調べたら出てくると思いますが、開発は基本的に
1. ページのソースをみる(chromeの場合、知りたい要素を右クリックして"検証")
2.　要素のidかxpathを調べてseleniumで指定
3.　click()クリック/ textで本文取得/ etc...
の流れです。

```python:arxivbot.py
#論文ごとのクラス
class Entry():

##initとtranslate関数がここにあります##
    
    def arrange_attachment(self):
        self.attachments = [
            {
                "fallback": "Required plain-text summary of the attachment.",
                "color": "#2eb886",
                "fields": [
                    {
                        "value": self.title+"\n"+self.title_ja+"\n"+self.url,
                        "short": False
                    }
                ],
                "image_url": "http://my-website.com/path/to/image.jpg",
                "thumb_url": "http://example.com/path/to/thumb.png",
            }
        ]


for one_entry in rss.entries:
    try:
        # Entryのクラスを作成
        entry = Entry(one_entry)
        # 翻訳
        entry.translate()
        # slackに送る情報(attachment)を作成
        entry.arrange_attachment()
        attachments = entry.attachments.copy()
        # 通知する
        slack.notify(attachments=attachments)
    #接続エラーやロボット検出があればストップ（多分問題なし）
    except:
        break
```

slackのwebhookに情報を渡すにはslack.notifyという関数を利用しますが、ここでattachmentsという引数に通知したい情報を辞書形式で渡します。
このattachmentの書き方の詳細は[slack webhook公式ページ](https://api.slack.com/docs/message-attachments)からみれます。

これでSlackに通知がくるかと思います！
(しかし通知がうるさいので、通知音やホーム画面への通知は切っておくことをオススメします笑)

#cron
ここまででarxivの新着情報をpythonによってslackに送ることができました。
次はcronというデーモンプログラムで時間や日付を指定して、バックグラウンドで先ほど作ったarxivbot.pyを実行させます。ちなみにwikiによるとデーモンという言葉はdemon(悪魔)とdeamon(守護神)の２通りの書き方が使われてるみたいですね。厨二でかっこいい（）

やることは非常に簡単で、

```
crontab -e
```
でcronの設定ファイルを開き、

```
0 9 * * * {pythonのpath} {arxiv_botのpath}
```
と書くだけです。
ここで、{pythonのpath}は

```
which python
```
の結果で、{arxiv_botのpath}はarxiv_botの絶対PATHです。

0 9 * * * というのは
0分 9時 *日 *月 *年ということで、つまり毎日9時0分に実行するということになります。

これが出来ればbotの作成が終了です。お疲れ様でした。