CVPR2020の論文を毎日自動で送ってくれるslack botを作ってみた！

## 経緯
最近は[CVPR2020](https://openaccess.thecvf.com/CVPR2020)の論文の要約を読んで、面白そうな論文は[mendeley](https://www.mendeley.com/newsfeed)にストックするという作業をやっています。その作業が結構大変です。理由は以下の通りです。

- **採択されている論文が多い**(1500ぐらいだったような)
- **あんまり興味がない論文が結構ある。**Adversarial attacksの防御論文多すぎ(笑)。
- わからない英単語が結構あるので、**google翻訳**でその都度調べる

このような理由でどうにか作業をもっと簡単にしたいと思い、以下のようなシステムを作ることにしました。

## プログラムの手順
1. スクレイピングでCVPR2020の[公式サイト](https://openaccess.thecvf.com/CVPR2020)から**論文のタイトル**、**要約**、**pdfのリンク**を取ってくる。
2. 要約を和訳する。
3. 要約の中で、あまり興味がない内容の単語が入っている場合は別にする。今回はAdversarial attacks系の論文。
4. 論文のタイトル、要約、要約を和訳したもの、pdfのリンクを1時間おきに**slackのチャンネルに自動で配信する**。

手順ごとに自分の実装を説明していきます。

## 1. スクレイピング
```python
def main():
    # CVPR2020のサイト情報を取得
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0"
        }
    url_name = "https://openaccess.thecvf.com/CVPR2020"
    url = requests.get(url_name, headers)
    soup = BeautifulSoup(url.content, "html.parser")
    # 論文情報がdtダグの中にあるのでその情報を取得する
    elems = soup.find_all("dt")
    for elem in tqdm(elems):
        url = elem.next.next.attrs['href']
        ori_link = "https://openaccess.thecvf.com/"
        contents_url = ori_link + url
        paper_link = requests.get(contents_url, headers)
        paper_soup = BeautifulSoup(paper_link.content, "html.parser")
        title = paper_soup.find('div', id="papertitle").text
        abstract = paper_soup.find('div', id="abstract").text
```
beautfulsoupを使っています。CVPRの論文はサイト上で以下のような感じで並んでいます。
![Screenshot from 2020-07-19 20-46-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663246/f4e7004d-51a9-d642-74b0-9c89377f81e1.png)
それぞれのタイトルをクリックするとその論文の詳細ページに飛びます。
![Screenshot from 2020-07-19 20-48-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663246/1ecf4d5c-3363-719f-24a9-145d046e91cd.png)
このページから論文のタイトル、要約、pdfのリンク(下のリンク)をスクレイピングします。

## 2. 要約の和訳
```
abstract_ja = translator.translate(abstract, src='en', dest='ja')
abstract_ja = str(abstract_ja.text)
```
googletransを使いました。[こちら](https://96lovefootball.hatenablog.com/entry/2019/02/10/213000)を参考にしました。

## 3. Adversarial attacksの排除
```python
def sort_class(abstract):
    sentence = abstract
    adversarial_1 = re.search("adversarial attacks", sentence)
    adversarial_2 = re.search("Adversarial Attacks", sentence)
    adversarial_3 = re.search("adversarial examples", sentence)
    adversarial_4 = re.search("Adversarial Examples", sentence)
    adversarial_5 = re.search("adversarial robustness", sentence)
    adversarial_6 = re.search("Adversarial Robustness", sentence)
    adversarial_7 = re.search("adversarial noise", sentence)
    adversarial_8 = re.search("Adversarial Noise", sentence)
    if adversarial_1 != None or adversarial_2 != None or adversarial_3 != None or adversarial_4 != None or adversarial_5 != None or adversarial_6 != None or adversarial_7 != None or adversarial_8 != None:
        class_name = "adver"
        return class_name
    else:
        class_name = "other"
        return class_name
```
ちゃんと正規表現をやればもっと簡単にできると思います。今回は馬鹿正直に候補を全て書きました。adversarial attacksの論文は別のチャンネルに上げるようにします。


## 4. slackに自動配信
slackwebを使いました。使い方を[こちら](https://qiita.com/satoshi03/items/14495bf431b1932cb90b)を参考にしました。コード全体を下にまとめます。

```python:post_to_slack.py
import re
import time
import slackweb
import requests
from tqdm import tqdm
from bs4 import BeautifulSoup
from googletrans import Translator
translator = Translator()


def sort_class(abstract):
    sentence = abstract
    adversarial_1 = re.search("adversarial attacks", sentence)
    adversarial_2 = re.search("Adversarial Attacks", sentence)
    adversarial_3 = re.search("adversarial examples", sentence)
    adversarial_4 = re.search("Adversarial Examples", sentence)
    adversarial_5 = re.search("adversarial robustness", sentence)
    adversarial_6 = re.search("Adversarial Robustness", sentence)
    adversarial_7 = re.search("adversarial noise", sentence)
    adversarial_8 = re.search("Adversarial Noise", sentence)

    # 対象の単語がある場合は別にしておく
    if adversarial_1 != None or adversarial_2 != None or adversarial_3 != None or adversarial_4 != None or adversarial_5 != None or adversarial_6 != None or adversarial_7 != None or adversarial_8 != None:
        class_name = "adver"
        return class_name
    else:
        class_name = "other"
        return class_name


def main():
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0"
        }
    url_name = "https://openaccess.thecvf.com/CVPR2020"
    url = requests.get(url_name, headers)
    soup = BeautifulSoup(url.content, "html.parser")
    elems = soup.find_all("dt")
    for elem in tqdm(elems):
        url = elem.next.next.attrs['href']
        ori_link = "https://openaccess.thecvf.com/"
        contents_url = ori_link + url
        paper_link = requests.get(contents_url, headers)
        paper_soup = BeautifulSoup(paper_link.content, "html.parser")
        title = paper_soup.find('div', id="papertitle").text
        abstract = paper_soup.find('div', id="abstract").text
        abstract_ja = translator.translate(abstract, src='en', dest='ja')
        abstract_ja = str(abstract_ja.text)
        pdf = paper_soup.find("dd").find("a").attrs['href'][6:]
        pdf_link = ori_link + pdf
        class_name = sort_class(abstract)

        attachments_title = []
        attachments_contents = []
        paper_title = {"title": title,
                "text": pdf_link}
        paper_contents = {"title": abstract,
                "text": abstract_ja}
        attachments_title.append(paper_title)
        attachments_contents.append(paper_contents)

        # adversarial attacksとその他の論文で分けてslackに通知する
        if class_name == "adver":
            slack = slackweb.Slack(url="YOUR_TOKEN")
            slack.notify(attachments=attachments_title)
            slack.notify(attachments=attachments_contents)
        else:
            slack = slackweb.Slack(url="YOUR_TOKEN")
            slack.notify(attachments=attachments_title)
            slack.notify(attachments=attachments_contents)

        time.sleep(3600)


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

## 完成したslack bot
![Screenshot from 2020-07-19 21-09-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663246/865c9dc8-d601-a0a2-6866-3c64860f5e03.png)
論文のタイトル、pdfのリンク先、要約、要約を和訳したものの順で並んでいます。これが1時間ごとに自動で配信されます。herokuにデプロイしました。

## 感想
スクレイピングめっちゃ楽しかったです。ちょっとハマりそうです(笑)。ちなみに[こちら](https://join.slack.com/t/ikuikuworkspace/shared_invite/zt-ggwc6mg1-t~VrB9uTIup7uzi~NEFCyw)に今回のCVPRの論文も含めた様々な論文のチャンネルがあるslackのworkspaceを作ったので、お気軽に参加してください。コードの中でおかしいところとかありましたらコメントください。

**間違いや質問、ご意見等ありましたらお気軽にコメントください。頑張って答えますので(笑)。**