Paper2Slack~英語論文のアブストを要約・翻訳してSlackへ~


# はじめに
今回は論文のAbstractを抽出し、要約・翻訳を施してSlackへ送るアプリをPythonで作成しました。Abstractは論文を要約していますが、チャットで共有するには少し長いですよね。これを使うことで論文共有がスムーズになるかと思います！
<img width="" alt="result" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629975/dfd3626e-7605-7a7f-f881-f4faefb933e9.png">


# 目次
* 構成図
* プログラム全体
* Pythonによる実装
    1. PDFからAbstractを抽出
    2. Transformersによる要約
    3. DeepL APIによる翻訳
    4. SlackへPOST
* 終わりに
* 参考文献

# 構成図
<img width="500" alt="app_design" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629975/95a94ff1-80af-f783-4f19-c8bfbe38a54b.png">


# プログラム全体
```python:main.py
import os
import requests
from pdfminer.high_level import extract_text
from transformers import pipeline
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

### EXTRACT INFO FROM PDF ###
# Extract Abstract
text = extract_text("test_paper1.pdf") # ローカルPCにある対象の論文
text = text.replace("\n", "")
text = text[text.find("Abstract"):text.find("Introduction")]


### SUMMARIZATION ###
summarizer = pipeline("summarization")
response = summarizer(text, max_length=100, min_length=30, do_sample=False)
summarized_text = response[0]["summary_text"]


### TRANSLATE TEXT ###
# Settings
post_url = "https://api-free.deepl.com/v2/translate" # エンドポイント
api_key = "ご自身のAPI Key"
source_lang = "EN"
target_lang = "JA"

# Content
content = {
    "text":summarized_text,
    "auth_key":api_key,
    "source_lang":source_lang,
    "target_lang":target_lang
}
request = requests.post(post_url, data=content)
translated_text = request.json()["translations"][0]["text"] # レスポンスから翻訳済みテキストを取り出す

# Make return text
result = "【論文紹介】\n・要約\n" + summarized_text + "\n" + "・和訳\n" + translated_text


### POST TO SLACK ###
client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) # 環境変数のTokenを渡す

try:
    filepath="test_paper1.pdf" # 自身のファイル
    response = client.files_upload(channels='#test', file=filepath, initial_comment=result) # 自身のチャンネル
    assert response["file"]  # the uploaded file
except SlackApiError as e:
    # You will get a SlackApiError if "ok" is False
    assert e.response["ok"] is False
    assert e.response["error"]  # str like 'invalid_auth', 'channel_not_found'
    print(f"Got an error: {e.response['error']}")
```

# Pythonによる実装
では早速実装していきます。先に使用するライブラリをinstall/importしておきましょう。
```python:main.py
import os
import requests
from pdfminer.high_level import extract_text
from transformers import pipeline
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
```


### 1. PDFからAbstractを抽出
まず、論文PDFからAbstractに該当する箇所を抽出していきましょう。
コードは以下の通りです。
```python:main.py
### EXTRACT INFO FROM PDF ###
# Extract Abstract
text = extract_text("test_paper1.pdf") # ローカルPCにある対象の論文
text = text.replace("\n", "")
text = text[text.find("Abstract"):text.find("Introduction")]
```

まずpdfminerというライブラリのextract_textメソッドを使用することでPDF全体の文字データを取得します。次に、改行タグを削除しておきます。削除しておかないとその後のタスクで都合が悪いです。最後に、「Abstract」から「Introduction」までの文字列を抽出しています。

### 2. Transformersによる要約
次は抽出されたAbstractをさらに要約します。Hugging FaceのTransformersのパイプラインを使用して翻訳を行います。
```python:main.py
### SUMMARIZATION ###
summarizer = pipeline("summarization")
response = summarizer(text, max_length=100, min_length=30, do_sample=False)
summarized_text = response[0]["summary_text"]
```
1~2行程度に要約してほしいので、30~100語の範囲を指定しました。


### 3. DeepL APIによる翻訳
翻訳にはDeepLのAPIを使用しました。アカウントを作成すれば、無料枠で月500,000文字まで翻訳可能です。Abstractだけを使えばそうそう使い切ることはないと思います。

```python:main.py
### TRANSLATE TEXT ###
# Settings
post_url = "https://api-free.deepl.com/v2/translate" # エンドポイント
api_key = "ご自身のAPI Key"
source_lang = "EN"
target_lang = "JA"

# Content
content = {
    "text":summarized_text,
    "auth_key":api_key,
    "source_lang":source_lang,
    "target_lang":target_lang
}

request = requests.post(post_url, data=content)
translated_text = request.json()["translations"][0]["text"] # レスポンスから翻訳済みテキストを取り出す

# Make return text
result = "【論文紹介】\n・要約\n" + summarized_text + "\n" + "・和訳\n" + translated_text
```
アカウント登録するとご自身のAPI Keyが発行されますので、そちらを入力してください。

### 4. SlackへPOST
最後に要約・翻訳済みのAbstractをSlackへ投稿しましょう。まずは[公式Document](https://api.slack.com/tutorials/uploading-files-with-python)に従い、投稿したいチャンネルにアプリを作成してください。途中環境変数の設定が必要になります。ファイルのアップロードのコードは[GitHub](https://github.com/slackapi/python-slack-sdk)のサンプルコードをそのまま真似しました。

```python:main.py
### POST TO SLACK ###
client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) # 環境変数のTokenを渡す

try:
    filepath="test_paper1.pdf" # 自身のファイル
    response = client.files_upload(channels='#test', file=filepath, initial_comment=result) # 自身のチャンネル
    assert response["file"]  # the uploaded file
except SlackApiError as e:
    # You will get a SlackApiError if "ok" is False
    assert e.response["ok"] is False
    assert e.response["error"]  # str like 'invalid_auth', 'channel_not_found'
    print(f"Got an error: {e.response['error']}")
```

# おわりに
以上で完成です！
今後の課題としては、
* 「Abstract」と「Introduction」に反応するようにしているため、「Summary」とかには対応できていない件

をどうにかしたいですね。

# 参考文献
* [pdfminer 公式Document](https://pdfminersix.readthedocs.io/en/latest/tutorial/highlevel.html)
* [Hugging Face 公式Document](https://huggingface.co/docs/transformers/main_classes/pipelines)
* [DeepL API 公式Document](https://www.deepl.com/ja/docs-api)
* [slack API 公式Document](https://api.slack.com/tutorials/uploading-files-with-python)
* [slack API GitHub](https://github.com/slackapi/python-slack-sdk)